@pkgseer/cli 0.2.4 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +141 -52
- package/dist/cli.js +50 -42
- package/dist/index.js +1 -1
- package/dist/shared/{chunk-48mwa8wt.js → chunk-drz16bhv.js} +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,120 +2,209 @@
|
|
|
2
2
|
|
|
3
3
|
CLI and MCP server for [PkgSeer](https://pkgseer.dev) — package intelligence for developers and AI assistants.
|
|
4
4
|
|
|
5
|
-
Get insights about packages across npm, PyPI, and
|
|
5
|
+
Get insights about packages across npm, PyPI, Hex, and crates.io registries: search code and documentation, check security vulnerabilities, analyze dependencies, and compare packages. Works standalone or as an MCP server for AI assistants like Claude and Cursor.
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Use npx without installation:
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
12
|
npx @pkgseer/cli --help
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
Or install globally:
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
18
|
npm install -g @pkgseer/cli
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
##
|
|
21
|
+
## Quick Start
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
```bash
|
|
24
|
+
# Interactive setup (recommended for first-time users)
|
|
25
|
+
pkgseer init
|
|
26
|
+
|
|
27
|
+
# Or set up manually:
|
|
28
|
+
pkgseer login # Authenticate with your PkgSeer account
|
|
29
|
+
pkgseer skill init # Install as AI assistant skill
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## AI Assistant Integration
|
|
33
|
+
|
|
34
|
+
PkgSeer works with AI assistants in two ways:
|
|
24
35
|
|
|
25
|
-
|
|
36
|
+
### Skills
|
|
37
|
+
|
|
38
|
+
Skills teach your AI assistant to use PkgSeer CLI commands through natural language:
|
|
26
39
|
|
|
27
40
|
```bash
|
|
28
|
-
pkgseer
|
|
41
|
+
pkgseer skill init
|
|
29
42
|
```
|
|
30
43
|
|
|
31
|
-
This
|
|
44
|
+
This installs a skill definition for Claude Code or Codex CLI. The AI runs CLI commands and reads the output.
|
|
45
|
+
|
|
46
|
+
### MCP Server
|
|
32
47
|
|
|
33
|
-
|
|
48
|
+
MCP provides structured tools that AI assistants can call programmatically:
|
|
34
49
|
|
|
35
50
|
```bash
|
|
36
|
-
pkgseer
|
|
51
|
+
pkgseer mcp init
|
|
37
52
|
```
|
|
38
53
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
The main use case for this CLI is as an MCP (Model Context Protocol) server that gives AI assistants access to package intelligence.
|
|
54
|
+
This provides structured tools that AI assistants can call programmatically. Configuration varies by assistant:
|
|
42
55
|
|
|
43
|
-
|
|
56
|
+
**Claude Code / Codex CLI**: The `mcp init` command configures these automatically.
|
|
44
57
|
|
|
45
|
-
Add
|
|
58
|
+
**Cursor IDE**: Add to `.cursor/mcp.json`:
|
|
46
59
|
|
|
47
60
|
```json
|
|
48
61
|
{
|
|
49
62
|
"mcpServers": {
|
|
50
63
|
"pkgseer": {
|
|
51
64
|
"command": "npx",
|
|
52
|
-
"args": ["-y", "@pkgseer/cli", "mcp"]
|
|
65
|
+
"args": ["-y", "@pkgseer/cli", "mcp", "start"]
|
|
53
66
|
}
|
|
54
67
|
}
|
|
55
68
|
}
|
|
56
69
|
```
|
|
57
70
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
Add this to your Claude Desktop configuration file:
|
|
61
|
-
|
|
62
|
-
**macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
63
|
-
**Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
71
|
+
**Claude Desktop**: Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS):
|
|
64
72
|
|
|
65
73
|
```json
|
|
66
74
|
{
|
|
67
75
|
"mcpServers": {
|
|
68
76
|
"pkgseer": {
|
|
69
77
|
"command": "npx",
|
|
70
|
-
"args": ["-y", "@pkgseer/cli", "mcp"]
|
|
78
|
+
"args": ["-y", "@pkgseer/cli", "mcp", "start"]
|
|
71
79
|
}
|
|
72
80
|
}
|
|
73
81
|
}
|
|
74
82
|
```
|
|
75
83
|
|
|
76
|
-
|
|
84
|
+
## CLI Commands
|
|
77
85
|
|
|
78
|
-
|
|
79
|
-
- "Compare react vs preact vs solid-js"
|
|
80
|
-
- "What are the dependencies of express?"
|
|
86
|
+
### Search
|
|
81
87
|
|
|
82
|
-
|
|
88
|
+
Search code and documentation across packages:
|
|
83
89
|
|
|
84
|
-
|
|
90
|
+
```bash
|
|
91
|
+
# Search in specific packages
|
|
92
|
+
pkgseer search "authentication" -P express,passport
|
|
93
|
+
pkgseer search "http client" -P pypi:requests,pypi:httpx
|
|
94
|
+
pkgseer search "json parsing" -P hex:jason,hex:poison
|
|
95
|
+
|
|
96
|
+
# Search modes
|
|
97
|
+
pkgseer search "auth" -P lodash --mode code # Code only
|
|
98
|
+
pkgseer search "auth" -P lodash --mode docs # Docs only
|
|
99
|
+
```
|
|
85
100
|
|
|
86
|
-
|
|
87
|
-
When working with dependencies:
|
|
101
|
+
### Package Commands
|
|
88
102
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
103
|
+
```bash
|
|
104
|
+
pkgseer pkg info lodash # Package summary and metadata
|
|
105
|
+
pkgseer pkg vulns lodash@4.17.21 # Security vulnerabilities
|
|
106
|
+
pkgseer pkg quality express # Quality score (0-100)
|
|
107
|
+
pkgseer pkg deps express # Direct dependencies
|
|
108
|
+
pkgseer pkg deps express --transitive # Include transitive deps
|
|
109
|
+
pkgseer pkg compare axios got fetch-h2 # Compare packages
|
|
92
110
|
```
|
|
93
111
|
|
|
94
|
-
|
|
112
|
+
Package format: `[registry:]name[@version]`
|
|
113
|
+
- `lodash` — npm (default registry)
|
|
114
|
+
- `pypi:requests` — PyPI
|
|
115
|
+
- `hex:phoenix` — Hex
|
|
116
|
+
- `crates:serde` — crates.io
|
|
117
|
+
- `lodash@4.17.21` — specific version
|
|
118
|
+
|
|
119
|
+
### Documentation Commands
|
|
95
120
|
|
|
96
|
-
|
|
121
|
+
```bash
|
|
122
|
+
pkgseer docs list pypi:requests # List available doc pages
|
|
123
|
+
pkgseer docs get lodash/chunk # Fetch specific doc page
|
|
124
|
+
pkgseer docs search "routing" -P express # Search docs only
|
|
125
|
+
```
|
|
97
126
|
|
|
98
|
-
|
|
99
|
-
| ------------------------- | ----------------------------------------------------------------------------------- |
|
|
100
|
-
| `package_summary` | Get package metadata, latest versions, security advisories, and quickstart examples |
|
|
101
|
-
| `package_vulnerabilities` | Find known security vulnerabilities affecting a package |
|
|
102
|
-
| `package_dependencies` | Explore the dependency tree (direct and transitive) |
|
|
103
|
-
| `package_quality` | View quality metrics and maintenance scores |
|
|
104
|
-
| `compare_packages` | Compare multiple packages side-by-side |
|
|
127
|
+
### Project Commands
|
|
105
128
|
|
|
106
|
-
|
|
129
|
+
```bash
|
|
130
|
+
pkgseer project init # Create pkgseer.yml config
|
|
131
|
+
pkgseer project detect # Detect package manifests
|
|
132
|
+
pkgseer project upload # Upload project to PkgSeer
|
|
133
|
+
```
|
|
107
134
|
|
|
108
|
-
|
|
135
|
+
### Authentication
|
|
109
136
|
|
|
110
137
|
```bash
|
|
111
|
-
pkgseer
|
|
112
|
-
pkgseer
|
|
138
|
+
pkgseer login # Authenticate via browser
|
|
139
|
+
pkgseer logout # Sign out
|
|
140
|
+
pkgseer auth status # Check authentication state
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Configuration
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
pkgseer config show # Display current configuration
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## MCP Tools
|
|
150
|
+
|
|
151
|
+
When running as an MCP server, these tools are available:
|
|
152
|
+
|
|
153
|
+
| Tool | Description |
|
|
154
|
+
|------|-------------|
|
|
155
|
+
| `package_summary` | Package metadata, versions, quickstart examples |
|
|
156
|
+
| `package_vulnerabilities` | Security advisories and CVEs |
|
|
157
|
+
| `package_dependencies` | Dependency tree (direct and transitive) |
|
|
158
|
+
| `package_quality` | Quality score with category breakdown |
|
|
159
|
+
| `compare_packages` | Side-by-side comparison of packages |
|
|
160
|
+
| `list_package_docs` | Available documentation pages |
|
|
161
|
+
| `fetch_package_doc` | Full content of a documentation page |
|
|
162
|
+
| `search` | Search code and docs across packages |
|
|
163
|
+
| `fetch_code_context` | Fetch code from search results |
|
|
164
|
+
| `search_project_docs` | Search docs for packages in current project |
|
|
165
|
+
|
|
166
|
+
## Configuration
|
|
167
|
+
|
|
168
|
+
### Project Configuration
|
|
113
169
|
|
|
114
|
-
pkgseer
|
|
115
|
-
pkgseer logout # Sign out and clear stored credentials
|
|
116
|
-
pkgseer auth status # Check if you're logged in and token validity
|
|
170
|
+
Create `pkgseer.yml` in your project root:
|
|
117
171
|
|
|
118
|
-
|
|
172
|
+
```yaml
|
|
173
|
+
project: my-project-name
|
|
174
|
+
|
|
175
|
+
# Optional: limit which tools are available
|
|
176
|
+
enabled_tools:
|
|
177
|
+
- package_summary
|
|
178
|
+
- package_vulnerabilities
|
|
179
|
+
- search_project_docs
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Environment Variables
|
|
183
|
+
|
|
184
|
+
| Variable | Description |
|
|
185
|
+
|----------|-------------|
|
|
186
|
+
| `PKGSEER_API_TOKEN` | API token (alternative to `pkgseer login`) |
|
|
187
|
+
| `PKGSEER_URL` | Base URL for PkgSeer (for development/testing) |
|
|
188
|
+
|
|
189
|
+
## Documentation
|
|
190
|
+
|
|
191
|
+
- [Architecture Overview](docs/implementation/architecture.md)
|
|
192
|
+
- [MCP Installation Guide](docs/implementation/mcp-installation.md)
|
|
193
|
+
- [Creating MCP Tools](docs/implementation/tools.md)
|
|
194
|
+
- [Authentication Flow](docs/implementation/auth.md)
|
|
195
|
+
- [Skills System](docs/implementation/skills.md)
|
|
196
|
+
- [Configuration Reference](docs/implementation/configuration.md)
|
|
197
|
+
|
|
198
|
+
## Development
|
|
199
|
+
|
|
200
|
+
See [CLAUDE.md](CLAUDE.md) for development guidelines.
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
bun install # Install dependencies
|
|
204
|
+
bun run dev # Development mode
|
|
205
|
+
bun test # Run tests
|
|
206
|
+
bun run build # Build for production
|
|
207
|
+
bun run codegen # Regenerate GraphQL types
|
|
119
208
|
```
|
|
120
209
|
|
|
121
210
|
## Need Help?
|
|
@@ -125,4 +214,4 @@ pkgseer mcp # Start the MCP server (for AI assistant integration)
|
|
|
125
214
|
|
|
126
215
|
## License
|
|
127
216
|
|
|
128
|
-
|
|
217
|
+
(c) 2025-2026 Juha Litola
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
version
|
|
4
|
-
} from "./shared/chunk-
|
|
4
|
+
} from "./shared/chunk-drz16bhv.js";
|
|
5
5
|
|
|
6
6
|
// src/cli.ts
|
|
7
7
|
import { Command } from "commander";
|
|
@@ -1789,7 +1789,7 @@ function parsePackageSpec(spec) {
|
|
|
1789
1789
|
if (spec.includes(":")) {
|
|
1790
1790
|
const colonIndex = spec.indexOf(":");
|
|
1791
1791
|
const potentialRegistry = spec.slice(0, colonIndex).toLowerCase();
|
|
1792
|
-
if (["npm", "pypi", "hex"].includes(potentialRegistry)) {
|
|
1792
|
+
if (["npm", "pypi", "hex", "crates"].includes(potentialRegistry)) {
|
|
1793
1793
|
registry = potentialRegistry;
|
|
1794
1794
|
rest = spec.slice(colonIndex + 1);
|
|
1795
1795
|
}
|
|
@@ -1808,7 +1808,8 @@ function toGraphQLRegistry(registry) {
|
|
|
1808
1808
|
const map = {
|
|
1809
1809
|
npm: "NPM",
|
|
1810
1810
|
pypi: "PYPI",
|
|
1811
|
-
hex: "HEX"
|
|
1811
|
+
hex: "HEX",
|
|
1812
|
+
crates: "CRATES"
|
|
1812
1813
|
};
|
|
1813
1814
|
return map[registry.toLowerCase()] || "NPM";
|
|
1814
1815
|
}
|
|
@@ -2559,7 +2560,7 @@ function abbrevLang(lang) {
|
|
|
2559
2560
|
function truncate(text, maxLen) {
|
|
2560
2561
|
if (text.length <= maxLen)
|
|
2561
2562
|
return text;
|
|
2562
|
-
return text.slice(0, maxLen - 3)
|
|
2563
|
+
return `${text.slice(0, maxLen - 3)}...`;
|
|
2563
2564
|
}
|
|
2564
2565
|
function formatEntryCompact(entry, useColors) {
|
|
2565
2566
|
if (!entry)
|
|
@@ -3319,7 +3320,7 @@ function shouldIgnorePath(relativePath, patterns) {
|
|
|
3319
3320
|
return ignored;
|
|
3320
3321
|
}
|
|
3321
3322
|
function shouldIgnoreDirectory(relativeDirPath, patterns) {
|
|
3322
|
-
const normalized = relativeDirPath.replace(/\\/g, "/").replace(/\/$/, "")
|
|
3323
|
+
const normalized = `${relativeDirPath.replace(/\\/g, "/").replace(/\/$/, "")}/`;
|
|
3323
3324
|
return shouldIgnorePath(normalized, patterns);
|
|
3324
3325
|
}
|
|
3325
3326
|
|
|
@@ -3352,6 +3353,10 @@ var MANIFEST_TYPES = [
|
|
|
3352
3353
|
{
|
|
3353
3354
|
type: "hex",
|
|
3354
3355
|
filenames: ["mix.exs", "mix.lock"]
|
|
3356
|
+
},
|
|
3357
|
+
{
|
|
3358
|
+
type: "crates",
|
|
3359
|
+
filenames: ["Cargo.toml", "Cargo.lock"]
|
|
3355
3360
|
}
|
|
3356
3361
|
];
|
|
3357
3362
|
function suggestLabel(relativePath) {
|
|
@@ -3360,7 +3365,7 @@ function suggestLabel(relativePath) {
|
|
|
3360
3365
|
if (parts.length === 1) {
|
|
3361
3366
|
return "root";
|
|
3362
3367
|
}
|
|
3363
|
-
return parts[parts.length - 2];
|
|
3368
|
+
return parts[parts.length - 2] ?? "root";
|
|
3364
3369
|
}
|
|
3365
3370
|
async function scanDirectoryRecursive(directory, fs, rootDir, options, currentDepth = 0, gitignorePatterns = null) {
|
|
3366
3371
|
const detected = [];
|
|
@@ -3426,12 +3431,12 @@ function filterRedundantPackageJson(manifests) {
|
|
|
3426
3431
|
if (!dirToManifests.has(dir)) {
|
|
3427
3432
|
dirToManifests.set(dir, []);
|
|
3428
3433
|
}
|
|
3429
|
-
dirToManifests.get(dir)
|
|
3434
|
+
dirToManifests.get(dir)?.push(manifest);
|
|
3430
3435
|
}
|
|
3431
3436
|
const filtered = [];
|
|
3432
|
-
for (const [
|
|
3437
|
+
for (const [_dir, dirManifests] of dirToManifests.entries()) {
|
|
3433
3438
|
const hasLockFile = dirManifests.some((m) => m.filename === "package-lock.json");
|
|
3434
|
-
const
|
|
3439
|
+
const _hasPackageJson = dirManifests.some((m) => m.filename === "package.json");
|
|
3435
3440
|
for (const manifest of dirManifests) {
|
|
3436
3441
|
if (manifest.filename === "package.json" && hasLockFile) {
|
|
3437
3442
|
continue;
|
|
@@ -4295,9 +4300,9 @@ Project already configured: ${highlight(existingConfig.config.project, useColors
|
|
|
4295
4300
|
setupProject = false;
|
|
4296
4301
|
} else {
|
|
4297
4302
|
console.log(`
|
|
4298
|
-
|
|
4303
|
+
${"=".repeat(50)}`);
|
|
4299
4304
|
console.log("Project Configuration Setup");
|
|
4300
|
-
console.log("=".repeat(50)
|
|
4305
|
+
console.log(`${"=".repeat(50)}
|
|
4301
4306
|
`);
|
|
4302
4307
|
await projectInit({}, {
|
|
4303
4308
|
projectService: deps.projectService,
|
|
@@ -4318,9 +4323,9 @@ ${highlight("✓", useColors)} Project setup complete!
|
|
|
4318
4323
|
}
|
|
4319
4324
|
if (aiIntegration === "skill") {
|
|
4320
4325
|
console.log(`
|
|
4321
|
-
|
|
4326
|
+
${"=".repeat(50)}`);
|
|
4322
4327
|
console.log("Skill Setup");
|
|
4323
|
-
console.log("=".repeat(50)
|
|
4328
|
+
console.log(`${"=".repeat(50)}
|
|
4324
4329
|
`);
|
|
4325
4330
|
await skillInit({
|
|
4326
4331
|
fileSystemService: deps.fileSystemService,
|
|
@@ -4334,9 +4339,9 @@ ${highlight("✓", useColors)} Skill setup complete!
|
|
|
4334
4339
|
const currentConfig = await deps.configService.loadProjectConfig();
|
|
4335
4340
|
const hasProjectNow = currentConfig?.config.project !== undefined;
|
|
4336
4341
|
console.log(`
|
|
4337
|
-
|
|
4342
|
+
${"=".repeat(50)}`);
|
|
4338
4343
|
console.log("MCP Server Setup");
|
|
4339
|
-
console.log("=".repeat(50)
|
|
4344
|
+
console.log(`${"=".repeat(50)}
|
|
4340
4345
|
`);
|
|
4341
4346
|
await mcpInit({
|
|
4342
4347
|
fileSystemService: deps.fileSystemService,
|
|
@@ -4352,7 +4357,7 @@ ${highlight("✓", useColors)} MCP setup complete!
|
|
|
4352
4357
|
}
|
|
4353
4358
|
console.log("=".repeat(50));
|
|
4354
4359
|
console.log("Setup Complete!");
|
|
4355
|
-
console.log("=".repeat(50)
|
|
4360
|
+
console.log(`${"=".repeat(50)}
|
|
4356
4361
|
`);
|
|
4357
4362
|
if (setupProject) {
|
|
4358
4363
|
const finalConfig = await deps.configService.loadProjectConfig();
|
|
@@ -4393,8 +4398,7 @@ function showCliUsage(useColors) {
|
|
|
4393
4398
|
console.log(` ${highlight("pkgseer docs get <pkg>/<page>", useColors)} Fetch doc content`);
|
|
4394
4399
|
console.log(` ${highlight("pkgseer docs search <query>", useColors)} Search documentation
|
|
4395
4400
|
`);
|
|
4396
|
-
console.log(dim(
|
|
4397
|
-
` + "Tip: Run 'pkgseer quickstart' for a quick reference guide.", useColors));
|
|
4401
|
+
console.log(dim("All commands support --json for structured output.", useColors));
|
|
4398
4402
|
}
|
|
4399
4403
|
function registerInitCommand(program) {
|
|
4400
4404
|
program.command("init").summary("Set up project and AI integration").description(`Set up PkgSeer for your project.
|
|
@@ -4463,9 +4467,11 @@ async function loginAction(options, deps) {
|
|
|
4463
4467
|
let callback;
|
|
4464
4468
|
try {
|
|
4465
4469
|
callback = await Promise.race([serverPromise, timeoutPromise]);
|
|
4466
|
-
|
|
4470
|
+
if (timeoutId)
|
|
4471
|
+
clearTimeout(timeoutId);
|
|
4467
4472
|
} catch (error2) {
|
|
4468
|
-
|
|
4473
|
+
if (timeoutId)
|
|
4474
|
+
clearTimeout(timeoutId);
|
|
4469
4475
|
if (error2 instanceof Error) {
|
|
4470
4476
|
console.log(`${error2.message}.
|
|
4471
4477
|
`);
|
|
@@ -4582,12 +4588,13 @@ function toGraphQLRegistry2(registry) {
|
|
|
4582
4588
|
const map = {
|
|
4583
4589
|
npm: "NPM",
|
|
4584
4590
|
pypi: "PYPI",
|
|
4585
|
-
hex: "HEX"
|
|
4591
|
+
hex: "HEX",
|
|
4592
|
+
crates: "CRATES"
|
|
4586
4593
|
};
|
|
4587
4594
|
return map[registry.toLowerCase()] || "NPM";
|
|
4588
4595
|
}
|
|
4589
4596
|
var schemas = {
|
|
4590
|
-
registry: z2.enum(["npm", "pypi", "hex"]).describe("Package registry (npm, pypi, or
|
|
4597
|
+
registry: z2.enum(["npm", "pypi", "hex", "crates"]).describe("Package registry (npm, pypi, hex, or crates)"),
|
|
4591
4598
|
packageName: z2.string().max(255).describe("Name of the package"),
|
|
4592
4599
|
version: z2.string().max(100).optional().describe("Specific version (defaults to latest)")
|
|
4593
4600
|
};
|
|
@@ -4630,7 +4637,7 @@ function notFoundError(packageName, registry) {
|
|
|
4630
4637
|
|
|
4631
4638
|
// src/tools/compare-packages.ts
|
|
4632
4639
|
var packageInputSchema = z3.object({
|
|
4633
|
-
registry: z3.enum(["npm", "pypi", "hex"]),
|
|
4640
|
+
registry: z3.enum(["npm", "pypi", "hex", "crates"]),
|
|
4634
4641
|
name: z3.string().max(255),
|
|
4635
4642
|
version: z3.string().max(100).optional()
|
|
4636
4643
|
});
|
|
@@ -4640,7 +4647,7 @@ var argsSchema = {
|
|
|
4640
4647
|
function createComparePackagesTool(pkgseerService) {
|
|
4641
4648
|
return {
|
|
4642
4649
|
name: "compare_packages",
|
|
4643
|
-
description: "Compare 2-10 packages side-by-side. Use this when evaluating alternatives (e.g., react vs preact vs solid-js). " + "Returns for each package: quality score, download counts, vulnerability count, license, and latest version. " + "Supports cross-registry comparison (npm, pypi, hex). " + 'Format: [{"registry":"npm","name":"lodash"},{"registry":"npm","name":"underscore"}]',
|
|
4650
|
+
description: "Compare 2-10 packages side-by-side. Use this when evaluating alternatives (e.g., react vs preact vs solid-js). " + "Returns for each package: quality score, download counts, vulnerability count, license, and latest version. " + "Supports cross-registry comparison (npm, pypi, hex, crates). " + 'Format: [{"registry":"npm","name":"lodash"},{"registry":"npm","name":"underscore"}]',
|
|
4644
4651
|
schema: argsSchema,
|
|
4645
4652
|
handler: async ({ packages }, _extra) => {
|
|
4646
4653
|
return withErrorHandling("compare packages", async () => {
|
|
@@ -5173,7 +5180,7 @@ function showMcpSetupInstructions(deps) {
|
|
|
5173
5180
|
console.log(` ${highlight(`${deps.baseUrl}/docs/mcp-server`, useColors)}
|
|
5174
5181
|
`);
|
|
5175
5182
|
console.log("Alternative: Use CLI directly (no MCP setup needed)");
|
|
5176
|
-
console.log(` ${highlight("pkgseer
|
|
5183
|
+
console.log(` ${highlight("pkgseer pkg info <package>", useColors)}`);
|
|
5177
5184
|
}
|
|
5178
5185
|
function registerMcpCommand(program) {
|
|
5179
5186
|
const mcpCommand = program.command("mcp").summary("Show setup instructions or start MCP server").description(`Start the Model Context Protocol (MCP) server using STDIO transport.
|
|
@@ -5255,10 +5262,10 @@ async function pkgCompareAction(packages, options, deps) {
|
|
|
5255
5262
|
if (options.json) {
|
|
5256
5263
|
const pkgs = result.data.comparePackages.packages?.filter((p) => p) ?? [];
|
|
5257
5264
|
const slim = pkgs.map((p) => ({
|
|
5258
|
-
package: `${p
|
|
5259
|
-
quality: p
|
|
5260
|
-
downloads: p
|
|
5261
|
-
vulnerabilities: p
|
|
5265
|
+
package: `${p?.packageName}@${p?.version}`,
|
|
5266
|
+
quality: p?.quality?.score,
|
|
5267
|
+
downloads: p?.downloadsLastMonth,
|
|
5268
|
+
vulnerabilities: p?.vulnerabilityCount
|
|
5262
5269
|
}));
|
|
5263
5270
|
output(slim, true);
|
|
5264
5271
|
} else {
|
|
@@ -5527,8 +5534,8 @@ async function pkgQualityAction(packageArg, options, deps) {
|
|
|
5527
5534
|
score: quality?.overallScore,
|
|
5528
5535
|
grade: quality?.grade,
|
|
5529
5536
|
categories: quality?.categories?.filter((c) => c).map((c) => ({
|
|
5530
|
-
name: c
|
|
5531
|
-
score: c
|
|
5537
|
+
name: c?.category,
|
|
5538
|
+
score: c?.score
|
|
5532
5539
|
}))
|
|
5533
5540
|
};
|
|
5534
5541
|
output(slim, true);
|
|
@@ -5629,10 +5636,10 @@ async function pkgVulnsAction(packageArg, options, deps) {
|
|
|
5629
5636
|
package: `${data.package?.name}@${data.package?.version}`,
|
|
5630
5637
|
count: data.security?.vulnerabilityCount ?? 0,
|
|
5631
5638
|
vulnerabilities: vulns.filter((v) => v).map((v) => ({
|
|
5632
|
-
id: v
|
|
5633
|
-
severity: v
|
|
5634
|
-
summary: v
|
|
5635
|
-
fixed: v
|
|
5639
|
+
id: v?.osvId,
|
|
5640
|
+
severity: v?.severityScore,
|
|
5641
|
+
summary: v?.summary,
|
|
5642
|
+
fixed: v?.fixedInVersions
|
|
5636
5643
|
}))
|
|
5637
5644
|
};
|
|
5638
5645
|
output(slim, true);
|
|
@@ -5673,7 +5680,7 @@ function matchManifestsWithConfig(detectedGroups, existingManifests) {
|
|
|
5673
5680
|
});
|
|
5674
5681
|
}
|
|
5675
5682
|
}
|
|
5676
|
-
const
|
|
5683
|
+
const _labelToFiles = new Map;
|
|
5677
5684
|
const labelToConfig = new Map;
|
|
5678
5685
|
for (const detectedGroup of detectedGroups) {
|
|
5679
5686
|
for (const manifest of detectedGroup.manifests) {
|
|
@@ -5686,13 +5693,14 @@ function matchManifestsWithConfig(detectedGroups, existingManifests) {
|
|
|
5686
5693
|
label = existingConfig?.label ?? detectedGroup.label;
|
|
5687
5694
|
}
|
|
5688
5695
|
const allowMixDeps = existingConfig?.allow_mix_deps;
|
|
5689
|
-
|
|
5690
|
-
|
|
5696
|
+
let config = labelToConfig.get(label);
|
|
5697
|
+
if (!config) {
|
|
5698
|
+
config = {
|
|
5691
5699
|
files: new Set,
|
|
5692
5700
|
allow_mix_deps: allowMixDeps
|
|
5693
|
-
}
|
|
5701
|
+
};
|
|
5702
|
+
labelToConfig.set(label, config);
|
|
5694
5703
|
}
|
|
5695
|
-
const config = labelToConfig.get(label);
|
|
5696
5704
|
config.files.add(manifest.relativePath);
|
|
5697
5705
|
if (allowMixDeps) {
|
|
5698
5706
|
config.allow_mix_deps = true;
|
|
@@ -5729,7 +5737,7 @@ function matchManifestsWithConfig(detectedGroups, existingManifests) {
|
|
|
5729
5737
|
});
|
|
5730
5738
|
}
|
|
5731
5739
|
async function projectDetectAction(options, deps) {
|
|
5732
|
-
const { configService, fileSystemService, promptService
|
|
5740
|
+
const { configService, fileSystemService, promptService } = deps;
|
|
5733
5741
|
const projectConfig = await configService.loadProjectConfig();
|
|
5734
5742
|
if (!projectConfig?.config.project) {
|
|
5735
5743
|
console.error(`✗ No project is configured in pkgseer.yml`);
|
|
@@ -5786,7 +5794,7 @@ Suggested configuration:`);
|
|
|
5786
5794
|
console.log(`${prefix}${file}`);
|
|
5787
5795
|
}
|
|
5788
5796
|
}
|
|
5789
|
-
const
|
|
5797
|
+
const _hasHexManifests = detectedGroups.some((group) => group.manifests.some((m) => m.type === "hex"));
|
|
5790
5798
|
const hasHexInSuggested = suggestedManifests.some((g) => g.files.some((f) => f.endsWith("mix.exs") || f.endsWith("mix.lock")));
|
|
5791
5799
|
let allowMixDeps = false;
|
|
5792
5800
|
if (hasHexInSuggested) {
|
package/dist/index.js
CHANGED