@pkgseer/cli 0.2.4 → 0.3.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/README.md +147 -52
- package/dist/cli.js +224 -88
- package/dist/index.js +1 -1
- package/dist/shared/{chunk-48mwa8wt.js → chunk-9yar14cw.js} +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,120 +2,215 @@
|
|
|
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:
|
|
35
|
+
|
|
36
|
+
### Skills
|
|
24
37
|
|
|
25
|
-
|
|
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.
|
|
32
45
|
|
|
33
|
-
|
|
46
|
+
### MCP Server
|
|
47
|
+
|
|
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
|
+
|
|
100
|
+
# Wait options (for packages that need indexing)
|
|
101
|
+
pkgseer search "api" -P new-package --wait 60000 # Wait up to 60s
|
|
102
|
+
pkgseer search "api" -P new-package --no-wait # Return immediately
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
If packages haven't been indexed yet, the search will wait up to 30 seconds by default. Use `--wait <ms>` to customize or `--no-wait` to return immediately with progress info.
|
|
85
106
|
|
|
86
|
-
|
|
87
|
-
When working with dependencies:
|
|
107
|
+
### Package Commands
|
|
88
108
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
109
|
+
```bash
|
|
110
|
+
pkgseer pkg info lodash # Package summary and metadata
|
|
111
|
+
pkgseer pkg vulns lodash@4.17.21 # Security vulnerabilities
|
|
112
|
+
pkgseer pkg quality express # Quality score (0-100)
|
|
113
|
+
pkgseer pkg deps express # Direct dependencies
|
|
114
|
+
pkgseer pkg deps express --transitive # Include transitive deps
|
|
115
|
+
pkgseer pkg compare axios got fetch-h2 # Compare packages
|
|
92
116
|
```
|
|
93
117
|
|
|
94
|
-
|
|
118
|
+
Package format: `[registry:]name[@version]`
|
|
119
|
+
- `lodash` — npm (default registry)
|
|
120
|
+
- `pypi:requests` — PyPI
|
|
121
|
+
- `hex:phoenix` — Hex
|
|
122
|
+
- `crates:serde` — crates.io
|
|
123
|
+
- `lodash@4.17.21` — specific version
|
|
124
|
+
|
|
125
|
+
### Documentation Commands
|
|
95
126
|
|
|
96
|
-
|
|
127
|
+
```bash
|
|
128
|
+
pkgseer docs list pypi:requests # List available doc pages
|
|
129
|
+
pkgseer docs get lodash/chunk # Fetch specific doc page
|
|
130
|
+
pkgseer docs search "routing" -P express # Search docs only
|
|
131
|
+
```
|
|
97
132
|
|
|
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 |
|
|
133
|
+
### Project Commands
|
|
105
134
|
|
|
106
|
-
|
|
135
|
+
```bash
|
|
136
|
+
pkgseer project init # Create pkgseer.yml config
|
|
137
|
+
pkgseer project detect # Detect package manifests
|
|
138
|
+
pkgseer project upload # Upload project to PkgSeer
|
|
139
|
+
```
|
|
107
140
|
|
|
108
|
-
|
|
141
|
+
### Authentication
|
|
109
142
|
|
|
110
143
|
```bash
|
|
111
|
-
pkgseer
|
|
112
|
-
pkgseer
|
|
144
|
+
pkgseer login # Authenticate via browser
|
|
145
|
+
pkgseer logout # Sign out
|
|
146
|
+
pkgseer auth status # Check authentication state
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Configuration
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
pkgseer config show # Display current configuration
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## MCP Tools
|
|
156
|
+
|
|
157
|
+
When running as an MCP server, these tools are available:
|
|
158
|
+
|
|
159
|
+
| Tool | Description |
|
|
160
|
+
|------|-------------|
|
|
161
|
+
| `package_summary` | Package metadata, versions, quickstart examples |
|
|
162
|
+
| `package_vulnerabilities` | Security advisories and CVEs |
|
|
163
|
+
| `package_dependencies` | Dependency tree (direct and transitive) |
|
|
164
|
+
| `package_quality` | Quality score with category breakdown |
|
|
165
|
+
| `compare_packages` | Side-by-side comparison of packages |
|
|
166
|
+
| `list_package_docs` | Available documentation pages |
|
|
167
|
+
| `fetch_package_doc` | Full content of a documentation page |
|
|
168
|
+
| `search` | Search code and docs across packages |
|
|
169
|
+
| `fetch_code_context` | Fetch code from search results |
|
|
170
|
+
| `search_project_docs` | Search docs for packages in current project |
|
|
171
|
+
|
|
172
|
+
## Configuration
|
|
173
|
+
|
|
174
|
+
### Project Configuration
|
|
113
175
|
|
|
114
|
-
pkgseer
|
|
115
|
-
pkgseer logout # Sign out and clear stored credentials
|
|
116
|
-
pkgseer auth status # Check if you're logged in and token validity
|
|
176
|
+
Create `pkgseer.yml` in your project root:
|
|
117
177
|
|
|
118
|
-
|
|
178
|
+
```yaml
|
|
179
|
+
project: my-project-name
|
|
180
|
+
|
|
181
|
+
# Optional: limit which tools are available
|
|
182
|
+
enabled_tools:
|
|
183
|
+
- package_summary
|
|
184
|
+
- package_vulnerabilities
|
|
185
|
+
- search_project_docs
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Environment Variables
|
|
189
|
+
|
|
190
|
+
| Variable | Description |
|
|
191
|
+
|----------|-------------|
|
|
192
|
+
| `PKGSEER_API_TOKEN` | API token (alternative to `pkgseer login`) |
|
|
193
|
+
| `PKGSEER_URL` | Base URL for PkgSeer (for development/testing) |
|
|
194
|
+
|
|
195
|
+
## Documentation
|
|
196
|
+
|
|
197
|
+
- [Architecture Overview](docs/implementation/architecture.md)
|
|
198
|
+
- [MCP Installation Guide](docs/implementation/mcp-installation.md)
|
|
199
|
+
- [Creating MCP Tools](docs/implementation/tools.md)
|
|
200
|
+
- [Authentication Flow](docs/implementation/auth.md)
|
|
201
|
+
- [Skills System](docs/implementation/skills.md)
|
|
202
|
+
- [Configuration Reference](docs/implementation/configuration.md)
|
|
203
|
+
|
|
204
|
+
## Development
|
|
205
|
+
|
|
206
|
+
See [CLAUDE.md](CLAUDE.md) for development guidelines.
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
bun install # Install dependencies
|
|
210
|
+
bun run dev # Development mode
|
|
211
|
+
bun test # Run tests
|
|
212
|
+
bun run build # Build for production
|
|
213
|
+
bun run codegen # Regenerate GraphQL types
|
|
119
214
|
```
|
|
120
215
|
|
|
121
216
|
## Need Help?
|
|
@@ -125,4 +220,4 @@ pkgseer mcp # Start the MCP server (for AI assistant integration)
|
|
|
125
220
|
|
|
126
221
|
## License
|
|
127
222
|
|
|
128
|
-
|
|
223
|
+
(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-9yar14cw.js";
|
|
5
5
|
|
|
6
6
|
// src/cli.ts
|
|
7
7
|
import { Command } from "commander";
|
|
@@ -211,36 +211,52 @@ var CliDocsListDocument = gql`
|
|
|
211
211
|
}
|
|
212
212
|
`;
|
|
213
213
|
var CombinedSearchDocument = gql`
|
|
214
|
-
query CombinedSearch($packages: [SearchPackageInput!]!, $query: String!, $mode: SearchMode, $limit: Int) {
|
|
215
|
-
combinedSearch(
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
214
|
+
query CombinedSearch($packages: [SearchPackageInput!]!, $query: String!, $mode: SearchMode, $limit: Int, $waitTimeoutMs: Int) {
|
|
215
|
+
combinedSearch(
|
|
216
|
+
packages: $packages
|
|
217
|
+
query: $query
|
|
218
|
+
mode: $mode
|
|
219
|
+
limit: $limit
|
|
220
|
+
waitTimeoutMs: $waitTimeoutMs
|
|
221
|
+
) {
|
|
222
|
+
completed
|
|
223
|
+
searchRef
|
|
224
|
+
result {
|
|
225
|
+
query
|
|
226
|
+
mode
|
|
227
|
+
totalResults
|
|
228
|
+
entries {
|
|
229
|
+
id
|
|
230
|
+
type
|
|
231
|
+
title
|
|
232
|
+
subtitle
|
|
233
|
+
packageName
|
|
234
|
+
registry
|
|
235
|
+
score
|
|
236
|
+
snippet
|
|
237
|
+
filePath
|
|
238
|
+
startLine
|
|
239
|
+
endLine
|
|
240
|
+
language
|
|
241
|
+
chunkType
|
|
242
|
+
repoUrl
|
|
243
|
+
gitRef
|
|
244
|
+
pageId
|
|
245
|
+
sourceUrl
|
|
246
|
+
}
|
|
247
|
+
indexingStatus {
|
|
248
|
+
registry
|
|
249
|
+
packageName
|
|
250
|
+
version
|
|
251
|
+
codeStatus
|
|
252
|
+
docsStatus
|
|
253
|
+
}
|
|
237
254
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
docsStatus
|
|
255
|
+
progress {
|
|
256
|
+
status
|
|
257
|
+
packagesTotal
|
|
258
|
+
packagesReady
|
|
259
|
+
elapsedMs
|
|
244
260
|
}
|
|
245
261
|
}
|
|
246
262
|
}
|
|
@@ -1419,7 +1435,8 @@ class PkgseerServiceImpl {
|
|
|
1419
1435
|
packages,
|
|
1420
1436
|
query,
|
|
1421
1437
|
mode: options?.mode,
|
|
1422
|
-
limit: options?.limit
|
|
1438
|
+
limit: options?.limit,
|
|
1439
|
+
waitTimeoutMs: options?.waitTimeoutMs
|
|
1423
1440
|
});
|
|
1424
1441
|
return { data: result.data, errors: result.errors };
|
|
1425
1442
|
}
|
|
@@ -1789,7 +1806,7 @@ function parsePackageSpec(spec) {
|
|
|
1789
1806
|
if (spec.includes(":")) {
|
|
1790
1807
|
const colonIndex = spec.indexOf(":");
|
|
1791
1808
|
const potentialRegistry = spec.slice(0, colonIndex).toLowerCase();
|
|
1792
|
-
if (["npm", "pypi", "hex"].includes(potentialRegistry)) {
|
|
1809
|
+
if (["npm", "pypi", "hex", "crates"].includes(potentialRegistry)) {
|
|
1793
1810
|
registry = potentialRegistry;
|
|
1794
1811
|
rest = spec.slice(colonIndex + 1);
|
|
1795
1812
|
}
|
|
@@ -1808,7 +1825,8 @@ function toGraphQLRegistry(registry) {
|
|
|
1808
1825
|
const map = {
|
|
1809
1826
|
npm: "NPM",
|
|
1810
1827
|
pypi: "PYPI",
|
|
1811
|
-
hex: "HEX"
|
|
1828
|
+
hex: "HEX",
|
|
1829
|
+
crates: "CRATES"
|
|
1812
1830
|
};
|
|
1813
1831
|
return map[registry.toLowerCase()] || "NPM";
|
|
1814
1832
|
}
|
|
@@ -2341,6 +2359,7 @@ function registerDocsListCommand(program) {
|
|
|
2341
2359
|
});
|
|
2342
2360
|
}
|
|
2343
2361
|
// src/commands/search.ts
|
|
2362
|
+
var DEFAULT_WAIT_TIMEOUT_MS = 30000;
|
|
2344
2363
|
var colors = {
|
|
2345
2364
|
reset: "\x1B[0m",
|
|
2346
2365
|
bold: "\x1B[1m",
|
|
@@ -2559,7 +2578,37 @@ function abbrevLang(lang) {
|
|
|
2559
2578
|
function truncate(text, maxLen) {
|
|
2560
2579
|
if (text.length <= maxLen)
|
|
2561
2580
|
return text;
|
|
2562
|
-
return text.slice(0, maxLen - 3)
|
|
2581
|
+
return `${text.slice(0, maxLen - 3)}...`;
|
|
2582
|
+
}
|
|
2583
|
+
function isSubstantiveLine(line) {
|
|
2584
|
+
const trimmed = line.trim();
|
|
2585
|
+
if (trimmed.length === 0)
|
|
2586
|
+
return false;
|
|
2587
|
+
if (/^[(){}[\];,]+$/.test(trimmed))
|
|
2588
|
+
return false;
|
|
2589
|
+
if (trimmed.length <= 3 && /^[^a-zA-Z0-9]*$/.test(trimmed))
|
|
2590
|
+
return false;
|
|
2591
|
+
return true;
|
|
2592
|
+
}
|
|
2593
|
+
function findBestSnippetLine(snippet) {
|
|
2594
|
+
const lines = snippet.split(`
|
|
2595
|
+
`).map((l) => l.trim());
|
|
2596
|
+
if (lines.length === 0)
|
|
2597
|
+
return null;
|
|
2598
|
+
const middleIndex = Math.floor((lines.length - 1) / 2);
|
|
2599
|
+
for (let offset = 0;offset <= lines.length; offset++) {
|
|
2600
|
+
const beforeIdx = middleIndex - offset;
|
|
2601
|
+
if (beforeIdx >= 0 && isSubstantiveLine(lines[beforeIdx])) {
|
|
2602
|
+
return lines[beforeIdx];
|
|
2603
|
+
}
|
|
2604
|
+
if (offset > 0) {
|
|
2605
|
+
const afterIdx = middleIndex + offset;
|
|
2606
|
+
if (afterIdx < lines.length && isSubstantiveLine(lines[afterIdx])) {
|
|
2607
|
+
return lines[afterIdx];
|
|
2608
|
+
}
|
|
2609
|
+
}
|
|
2610
|
+
}
|
|
2611
|
+
return null;
|
|
2563
2612
|
}
|
|
2564
2613
|
function formatEntryCompact(entry, useColors) {
|
|
2565
2614
|
if (!entry)
|
|
@@ -2578,12 +2627,11 @@ function formatEntryCompact(entry, useColors) {
|
|
|
2578
2627
|
}
|
|
2579
2628
|
let snippet = "";
|
|
2580
2629
|
if (entry.snippet) {
|
|
2581
|
-
const
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
snippet = ` ${truncate(firstLine, 80)}`;
|
|
2630
|
+
const bestLine = findBestSnippetLine(entry.snippet);
|
|
2631
|
+
if (bestLine) {
|
|
2632
|
+
snippet = ` ${truncate(bestLine, 80)}`;
|
|
2585
2633
|
if (useColors) {
|
|
2586
|
-
snippet = ` ${colors.dim}${truncate(
|
|
2634
|
+
snippet = ` ${colors.dim}${truncate(bestLine, 80)}${colors.reset}`;
|
|
2587
2635
|
}
|
|
2588
2636
|
}
|
|
2589
2637
|
}
|
|
@@ -2661,6 +2709,50 @@ function summarizeSearchResults(results) {
|
|
|
2661
2709
|
return lines.join(`
|
|
2662
2710
|
`);
|
|
2663
2711
|
}
|
|
2712
|
+
function formatSearchProgress(progress, searchRef, useColors) {
|
|
2713
|
+
const lines = [];
|
|
2714
|
+
if (useColors) {
|
|
2715
|
+
lines.push(`${colors.yellow}Search in progress...${colors.reset}`);
|
|
2716
|
+
} else {
|
|
2717
|
+
lines.push("Search in progress...");
|
|
2718
|
+
}
|
|
2719
|
+
if (progress) {
|
|
2720
|
+
const statusText = progress.status?.toLowerCase() ?? "unknown";
|
|
2721
|
+
lines.push(`Status: ${statusText}`);
|
|
2722
|
+
if (progress.packagesTotal != null && progress.packagesReady != null) {
|
|
2723
|
+
lines.push(`Packages: ${progress.packagesReady}/${progress.packagesTotal} ready`);
|
|
2724
|
+
}
|
|
2725
|
+
if (progress.elapsedMs != null) {
|
|
2726
|
+
lines.push(`Elapsed: ${(progress.elapsedMs / 1000).toFixed(1)}s`);
|
|
2727
|
+
}
|
|
2728
|
+
}
|
|
2729
|
+
lines.push("");
|
|
2730
|
+
lines.push("Some packages may still be indexing.");
|
|
2731
|
+
lines.push("Run again in a moment for complete results.");
|
|
2732
|
+
if (searchRef) {
|
|
2733
|
+
lines.push("");
|
|
2734
|
+
if (useColors) {
|
|
2735
|
+
lines.push(`${colors.dim}Search ref: ${searchRef}${colors.reset}`);
|
|
2736
|
+
} else {
|
|
2737
|
+
lines.push(`Search ref: ${searchRef}`);
|
|
2738
|
+
}
|
|
2739
|
+
}
|
|
2740
|
+
return lines.join(`
|
|
2741
|
+
`);
|
|
2742
|
+
}
|
|
2743
|
+
function getWaitTimeoutMs(options) {
|
|
2744
|
+
if (options.noWait) {
|
|
2745
|
+
return 0;
|
|
2746
|
+
}
|
|
2747
|
+
if (options.wait) {
|
|
2748
|
+
const parsed = Number.parseInt(options.wait, 10);
|
|
2749
|
+
if (!Number.isNaN(parsed) && parsed >= 0) {
|
|
2750
|
+
return parsed;
|
|
2751
|
+
}
|
|
2752
|
+
console.warn(`Warning: Invalid --wait value "${options.wait}". Using default (${DEFAULT_WAIT_TIMEOUT_MS}ms).`);
|
|
2753
|
+
}
|
|
2754
|
+
return DEFAULT_WAIT_TIMEOUT_MS;
|
|
2755
|
+
}
|
|
2664
2756
|
async function searchAction(queryArg, options, deps, defaultMode = "ALL") {
|
|
2665
2757
|
const { pkgseerService } = deps;
|
|
2666
2758
|
const query = Array.isArray(queryArg) ? queryArg.join(" ") : queryArg ?? "";
|
|
@@ -2680,16 +2772,37 @@ async function searchAction(queryArg, options, deps, defaultMode = "ALL") {
|
|
|
2680
2772
|
const limit = options.limit ? Number.parseInt(options.limit, 10) : 25;
|
|
2681
2773
|
const mode = options.mode ? toSearchMode(options.mode) : defaultMode;
|
|
2682
2774
|
const useColors = shouldUseColors(options.noColor);
|
|
2775
|
+
const waitTimeoutMs = getWaitTimeoutMs(options);
|
|
2683
2776
|
const result = await pkgseerService.combinedSearch(packages, query, {
|
|
2684
2777
|
mode,
|
|
2685
|
-
limit
|
|
2778
|
+
limit,
|
|
2779
|
+
waitTimeoutMs
|
|
2686
2780
|
});
|
|
2687
2781
|
handleErrors(result.errors, options.json ?? false);
|
|
2688
|
-
|
|
2782
|
+
const asyncResult = result.data.combinedSearch;
|
|
2783
|
+
if (!asyncResult) {
|
|
2689
2784
|
outputError("No results returned. Check package names and registries.", options.json ?? false);
|
|
2690
2785
|
return;
|
|
2691
2786
|
}
|
|
2692
|
-
|
|
2787
|
+
if (!asyncResult.completed) {
|
|
2788
|
+
if (options.json) {
|
|
2789
|
+
output({
|
|
2790
|
+
completed: false,
|
|
2791
|
+
searchRef: asyncResult.searchRef,
|
|
2792
|
+
progress: asyncResult.progress,
|
|
2793
|
+
message: "Search is still indexing. Retry for complete results."
|
|
2794
|
+
}, true);
|
|
2795
|
+
} else {
|
|
2796
|
+
console.log(formatSearchProgress(asyncResult.progress, asyncResult.searchRef, useColors));
|
|
2797
|
+
}
|
|
2798
|
+
return;
|
|
2799
|
+
}
|
|
2800
|
+
const searchResults = asyncResult.result;
|
|
2801
|
+
if (!searchResults) {
|
|
2802
|
+
outputError("Search completed but no results returned.", options.json ?? false);
|
|
2803
|
+
return;
|
|
2804
|
+
}
|
|
2805
|
+
outputResults(searchResults, options, useColors);
|
|
2693
2806
|
}
|
|
2694
2807
|
function outputResults(results, options, useColors) {
|
|
2695
2808
|
const format = options.json ? "json" : options.format ?? "human";
|
|
@@ -2740,7 +2853,7 @@ Examples:
|
|
|
2740
2853
|
# JSON output
|
|
2741
2854
|
pkgseer search "error" -P express --json`;
|
|
2742
2855
|
function addSearchOptions(cmd) {
|
|
2743
|
-
return cmd.option("-P, --packages <packages>", "Packages to search (comma-separated). Format: name or registry/name[@version]. Examples: express | express,lodash | pypi/django@4.2").option("-m, --mode <mode>", "Search mode: all (default), code, docs").option("-l, --limit <n>", "Max results (default: 25)").option("-v, --verbose", "Expanded output with more details").option("--refs-only", "Output only references (for piping)").option("--count", "Output result counts by package").option("--format <format>", "Output format: human|summary|json", "human").option("--no-color", "Disable colored output").option("--json", "Output as JSON");
|
|
2856
|
+
return cmd.option("-P, --packages <packages>", "Packages to search (comma-separated). Format: name or registry/name[@version]. Examples: express | express,lodash | pypi/django@4.2").option("-m, --mode <mode>", "Search mode: all (default), code, docs").option("-l, --limit <n>", "Max results (default: 25)").option("-v, --verbose", "Expanded output with more details").option("--refs-only", "Output only references (for piping)").option("--count", "Output result counts by package").option("--format <format>", "Output format: human|summary|json", "human").option("--no-color", "Disable colored output").option("--json", "Output as JSON").option("--wait <ms>", "Max milliseconds to wait for indexing (default: 30000)").option("--no-wait", "Return immediately without waiting for indexing");
|
|
2744
2857
|
}
|
|
2745
2858
|
function registerSearchCommand(program) {
|
|
2746
2859
|
const cmd = program.command("search [query...]").summary("Search code and documentation across packages").description(SEARCH_DESCRIPTION);
|
|
@@ -2781,7 +2894,7 @@ Examples:
|
|
|
2781
2894
|
|
|
2782
2895
|
Note: For code search, use 'pkgseer search --mode code' instead.`;
|
|
2783
2896
|
function addDocsSearchOptions(cmd) {
|
|
2784
|
-
return cmd.option("-P, --packages <packages>", "Packages to search (comma-separated). Format: [registry:]name[@version]. Examples: express | express,lodash | pypi:django@4.2").option("-l, --limit <n>", "Max results (default: 25)").option("-v, --verbose", "Expanded output with more details").option("--refs-only", "Output only references (for piping)").option("--count", "Output result counts by package").option("--format <format>", "Output format: human|summary|json", "human").option("--no-color", "Disable colored output").option("--json", "Output as JSON");
|
|
2897
|
+
return cmd.option("-P, --packages <packages>", "Packages to search (comma-separated). Format: [registry:]name[@version]. Examples: express | express,lodash | pypi:django@4.2").option("-l, --limit <n>", "Max results (default: 25)").option("-v, --verbose", "Expanded output with more details").option("--refs-only", "Output only references (for piping)").option("--count", "Output result counts by package").option("--format <format>", "Output format: human|summary|json", "human").option("--no-color", "Disable colored output").option("--json", "Output as JSON").option("--wait <ms>", "Max milliseconds to wait for indexing (default: 30000)").option("--no-wait", "Return immediately without waiting for indexing");
|
|
2785
2898
|
}
|
|
2786
2899
|
function registerDocsSearchCommand(program) {
|
|
2787
2900
|
const cmd = program.command("search [query...]").summary("Search documentation").description(DOCS_SEARCH_DESCRIPTION);
|
|
@@ -3319,7 +3432,7 @@ function shouldIgnorePath(relativePath, patterns) {
|
|
|
3319
3432
|
return ignored;
|
|
3320
3433
|
}
|
|
3321
3434
|
function shouldIgnoreDirectory(relativeDirPath, patterns) {
|
|
3322
|
-
const normalized = relativeDirPath.replace(/\\/g, "/").replace(/\/$/, "")
|
|
3435
|
+
const normalized = `${relativeDirPath.replace(/\\/g, "/").replace(/\/$/, "")}/`;
|
|
3323
3436
|
return shouldIgnorePath(normalized, patterns);
|
|
3324
3437
|
}
|
|
3325
3438
|
|
|
@@ -3352,6 +3465,10 @@ var MANIFEST_TYPES = [
|
|
|
3352
3465
|
{
|
|
3353
3466
|
type: "hex",
|
|
3354
3467
|
filenames: ["mix.exs", "mix.lock"]
|
|
3468
|
+
},
|
|
3469
|
+
{
|
|
3470
|
+
type: "crates",
|
|
3471
|
+
filenames: ["Cargo.toml", "Cargo.lock"]
|
|
3355
3472
|
}
|
|
3356
3473
|
];
|
|
3357
3474
|
function suggestLabel(relativePath) {
|
|
@@ -3360,7 +3477,7 @@ function suggestLabel(relativePath) {
|
|
|
3360
3477
|
if (parts.length === 1) {
|
|
3361
3478
|
return "root";
|
|
3362
3479
|
}
|
|
3363
|
-
return parts[parts.length - 2];
|
|
3480
|
+
return parts[parts.length - 2] ?? "root";
|
|
3364
3481
|
}
|
|
3365
3482
|
async function scanDirectoryRecursive(directory, fs, rootDir, options, currentDepth = 0, gitignorePatterns = null) {
|
|
3366
3483
|
const detected = [];
|
|
@@ -3426,12 +3543,12 @@ function filterRedundantPackageJson(manifests) {
|
|
|
3426
3543
|
if (!dirToManifests.has(dir)) {
|
|
3427
3544
|
dirToManifests.set(dir, []);
|
|
3428
3545
|
}
|
|
3429
|
-
dirToManifests.get(dir)
|
|
3546
|
+
dirToManifests.get(dir)?.push(manifest);
|
|
3430
3547
|
}
|
|
3431
3548
|
const filtered = [];
|
|
3432
|
-
for (const [
|
|
3549
|
+
for (const [_dir, dirManifests] of dirToManifests.entries()) {
|
|
3433
3550
|
const hasLockFile = dirManifests.some((m) => m.filename === "package-lock.json");
|
|
3434
|
-
const
|
|
3551
|
+
const _hasPackageJson = dirManifests.some((m) => m.filename === "package.json");
|
|
3435
3552
|
for (const manifest of dirManifests) {
|
|
3436
3553
|
if (manifest.filename === "package.json" && hasLockFile) {
|
|
3437
3554
|
continue;
|
|
@@ -4295,9 +4412,9 @@ Project already configured: ${highlight(existingConfig.config.project, useColors
|
|
|
4295
4412
|
setupProject = false;
|
|
4296
4413
|
} else {
|
|
4297
4414
|
console.log(`
|
|
4298
|
-
|
|
4415
|
+
${"=".repeat(50)}`);
|
|
4299
4416
|
console.log("Project Configuration Setup");
|
|
4300
|
-
console.log("=".repeat(50)
|
|
4417
|
+
console.log(`${"=".repeat(50)}
|
|
4301
4418
|
`);
|
|
4302
4419
|
await projectInit({}, {
|
|
4303
4420
|
projectService: deps.projectService,
|
|
@@ -4318,9 +4435,9 @@ ${highlight("✓", useColors)} Project setup complete!
|
|
|
4318
4435
|
}
|
|
4319
4436
|
if (aiIntegration === "skill") {
|
|
4320
4437
|
console.log(`
|
|
4321
|
-
|
|
4438
|
+
${"=".repeat(50)}`);
|
|
4322
4439
|
console.log("Skill Setup");
|
|
4323
|
-
console.log("=".repeat(50)
|
|
4440
|
+
console.log(`${"=".repeat(50)}
|
|
4324
4441
|
`);
|
|
4325
4442
|
await skillInit({
|
|
4326
4443
|
fileSystemService: deps.fileSystemService,
|
|
@@ -4334,9 +4451,9 @@ ${highlight("✓", useColors)} Skill setup complete!
|
|
|
4334
4451
|
const currentConfig = await deps.configService.loadProjectConfig();
|
|
4335
4452
|
const hasProjectNow = currentConfig?.config.project !== undefined;
|
|
4336
4453
|
console.log(`
|
|
4337
|
-
|
|
4454
|
+
${"=".repeat(50)}`);
|
|
4338
4455
|
console.log("MCP Server Setup");
|
|
4339
|
-
console.log("=".repeat(50)
|
|
4456
|
+
console.log(`${"=".repeat(50)}
|
|
4340
4457
|
`);
|
|
4341
4458
|
await mcpInit({
|
|
4342
4459
|
fileSystemService: deps.fileSystemService,
|
|
@@ -4352,7 +4469,7 @@ ${highlight("✓", useColors)} MCP setup complete!
|
|
|
4352
4469
|
}
|
|
4353
4470
|
console.log("=".repeat(50));
|
|
4354
4471
|
console.log("Setup Complete!");
|
|
4355
|
-
console.log("=".repeat(50)
|
|
4472
|
+
console.log(`${"=".repeat(50)}
|
|
4356
4473
|
`);
|
|
4357
4474
|
if (setupProject) {
|
|
4358
4475
|
const finalConfig = await deps.configService.loadProjectConfig();
|
|
@@ -4393,8 +4510,7 @@ function showCliUsage(useColors) {
|
|
|
4393
4510
|
console.log(` ${highlight("pkgseer docs get <pkg>/<page>", useColors)} Fetch doc content`);
|
|
4394
4511
|
console.log(` ${highlight("pkgseer docs search <query>", useColors)} Search documentation
|
|
4395
4512
|
`);
|
|
4396
|
-
console.log(dim(
|
|
4397
|
-
` + "Tip: Run 'pkgseer quickstart' for a quick reference guide.", useColors));
|
|
4513
|
+
console.log(dim("All commands support --json for structured output.", useColors));
|
|
4398
4514
|
}
|
|
4399
4515
|
function registerInitCommand(program) {
|
|
4400
4516
|
program.command("init").summary("Set up project and AI integration").description(`Set up PkgSeer for your project.
|
|
@@ -4463,9 +4579,11 @@ async function loginAction(options, deps) {
|
|
|
4463
4579
|
let callback;
|
|
4464
4580
|
try {
|
|
4465
4581
|
callback = await Promise.race([serverPromise, timeoutPromise]);
|
|
4466
|
-
|
|
4582
|
+
if (timeoutId)
|
|
4583
|
+
clearTimeout(timeoutId);
|
|
4467
4584
|
} catch (error2) {
|
|
4468
|
-
|
|
4585
|
+
if (timeoutId)
|
|
4586
|
+
clearTimeout(timeoutId);
|
|
4469
4587
|
if (error2 instanceof Error) {
|
|
4470
4588
|
console.log(`${error2.message}.
|
|
4471
4589
|
`);
|
|
@@ -4582,12 +4700,13 @@ function toGraphQLRegistry2(registry) {
|
|
|
4582
4700
|
const map = {
|
|
4583
4701
|
npm: "NPM",
|
|
4584
4702
|
pypi: "PYPI",
|
|
4585
|
-
hex: "HEX"
|
|
4703
|
+
hex: "HEX",
|
|
4704
|
+
crates: "CRATES"
|
|
4586
4705
|
};
|
|
4587
4706
|
return map[registry.toLowerCase()] || "NPM";
|
|
4588
4707
|
}
|
|
4589
4708
|
var schemas = {
|
|
4590
|
-
registry: z2.enum(["npm", "pypi", "hex"]).describe("Package registry (npm, pypi, or
|
|
4709
|
+
registry: z2.enum(["npm", "pypi", "hex", "crates"]).describe("Package registry (npm, pypi, hex, or crates)"),
|
|
4591
4710
|
packageName: z2.string().max(255).describe("Name of the package"),
|
|
4592
4711
|
version: z2.string().max(100).optional().describe("Specific version (defaults to latest)")
|
|
4593
4712
|
};
|
|
@@ -4630,7 +4749,7 @@ function notFoundError(packageName, registry) {
|
|
|
4630
4749
|
|
|
4631
4750
|
// src/tools/compare-packages.ts
|
|
4632
4751
|
var packageInputSchema = z3.object({
|
|
4633
|
-
registry: z3.enum(["npm", "pypi", "hex"]),
|
|
4752
|
+
registry: z3.enum(["npm", "pypi", "hex", "crates"]),
|
|
4634
4753
|
name: z3.string().max(255),
|
|
4635
4754
|
version: z3.string().max(100).optional()
|
|
4636
4755
|
});
|
|
@@ -4640,7 +4759,7 @@ var argsSchema = {
|
|
|
4640
4759
|
function createComparePackagesTool(pkgseerService) {
|
|
4641
4760
|
return {
|
|
4642
4761
|
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"}]',
|
|
4762
|
+
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
4763
|
schema: argsSchema,
|
|
4645
4764
|
handler: async ({ packages }, _extra) => {
|
|
4646
4765
|
return withErrorHandling("compare packages", async () => {
|
|
@@ -4944,11 +5063,13 @@ var packageInputSchema2 = z7.object({
|
|
|
4944
5063
|
name: z7.string().min(1).describe("Package name"),
|
|
4945
5064
|
version: z7.string().optional().describe("Specific version (defaults to latest)")
|
|
4946
5065
|
});
|
|
5066
|
+
var DEFAULT_AGENT_WAIT_TIMEOUT_MS = 1e4;
|
|
4947
5067
|
var argsSchema9 = {
|
|
4948
5068
|
packages: z7.array(packageInputSchema2).min(1).max(20).describe("Packages to search (1-20). Each package needs registry and name."),
|
|
4949
5069
|
query: z7.string().min(1).describe("Search query - natural language or keywords"),
|
|
4950
5070
|
mode: z7.enum(["all", "code", "docs"]).optional().describe('Search mode: "all" (default), "code" only, or "docs" only'),
|
|
4951
|
-
limit: z7.number().int().min(1).max(100).optional().describe("Maximum results (default: 20)")
|
|
5071
|
+
limit: z7.number().int().min(1).max(100).optional().describe("Maximum results (default: 20)"),
|
|
5072
|
+
waitTimeoutMs: z7.number().int().min(0).max(60000).optional().describe("Max milliseconds to wait for indexing (default: 10000, 0=immediate)")
|
|
4952
5073
|
};
|
|
4953
5074
|
function toSearchMode2(mode) {
|
|
4954
5075
|
if (!mode)
|
|
@@ -4986,9 +5107,9 @@ Results may be incomplete. Retry later for more complete results.`;
|
|
|
4986
5107
|
function createSearchTool(pkgseerService) {
|
|
4987
5108
|
return {
|
|
4988
5109
|
name: "search",
|
|
4989
|
-
description: "Search code and documentation across packages. Returns functions, classes, and documentation pages " + "matching your query. Use mode='code' for code only, mode='docs' for documentation only, or " + "mode='all' (default) for both. Provide 1-20 packages to search. Results include relevance scores " + "and snippets showing matches.",
|
|
5110
|
+
description: "Search code and documentation across packages. Returns functions, classes, and documentation pages " + "matching your query. Use mode='code' for code only, mode='docs' for documentation only, or " + "mode='all' (default) for both. Provide 1-20 packages to search. Results include relevance scores " + "and snippets showing matches. If packages need indexing, the search will wait up to waitTimeoutMs " + "(default 10s). If not complete, returns progress info with searchRef for follow-up.",
|
|
4990
5111
|
schema: argsSchema9,
|
|
4991
|
-
handler: async ({ packages, query, mode, limit }, _extra) => {
|
|
5112
|
+
handler: async ({ packages, query, mode, limit, waitTimeoutMs }, _extra) => {
|
|
4992
5113
|
return withErrorHandling("search packages", async () => {
|
|
4993
5114
|
const normalizedQuery = query.trim();
|
|
4994
5115
|
if (normalizedQuery.length === 0) {
|
|
@@ -5001,15 +5122,29 @@ function createSearchTool(pkgseerService) {
|
|
|
5001
5122
|
}));
|
|
5002
5123
|
const result = await pkgseerService.combinedSearch(graphqlPackages, normalizedQuery, {
|
|
5003
5124
|
mode: toSearchMode2(mode),
|
|
5004
|
-
limit
|
|
5125
|
+
limit,
|
|
5126
|
+
waitTimeoutMs: waitTimeoutMs ?? DEFAULT_AGENT_WAIT_TIMEOUT_MS
|
|
5005
5127
|
});
|
|
5006
5128
|
const graphqlError = handleGraphQLErrors(result.errors);
|
|
5007
5129
|
if (graphqlError)
|
|
5008
5130
|
return graphqlError;
|
|
5009
|
-
|
|
5131
|
+
const asyncResult = result.data.combinedSearch;
|
|
5132
|
+
if (!asyncResult) {
|
|
5010
5133
|
return errorResult("No search results returned. Check package names and registries.");
|
|
5011
5134
|
}
|
|
5012
|
-
|
|
5135
|
+
if (!asyncResult.completed) {
|
|
5136
|
+
const progressInfo = {
|
|
5137
|
+
completed: false,
|
|
5138
|
+
searchRef: asyncResult.searchRef,
|
|
5139
|
+
progress: asyncResult.progress,
|
|
5140
|
+
message: "Search is still indexing. Retry with same query for results, " + "or increase waitTimeoutMs to wait longer."
|
|
5141
|
+
};
|
|
5142
|
+
return textResult(JSON.stringify(progressInfo, null, 2));
|
|
5143
|
+
}
|
|
5144
|
+
const searchResult = asyncResult.result;
|
|
5145
|
+
if (!searchResult) {
|
|
5146
|
+
return errorResult("Search completed but no results returned.");
|
|
5147
|
+
}
|
|
5013
5148
|
const entries = searchResult.entries ?? [];
|
|
5014
5149
|
if (entries.length === 0) {
|
|
5015
5150
|
return errorResult(`No results found for "${normalizedQuery}". ` + "Try broader terms or different packages.");
|
|
@@ -5173,7 +5308,7 @@ function showMcpSetupInstructions(deps) {
|
|
|
5173
5308
|
console.log(` ${highlight(`${deps.baseUrl}/docs/mcp-server`, useColors)}
|
|
5174
5309
|
`);
|
|
5175
5310
|
console.log("Alternative: Use CLI directly (no MCP setup needed)");
|
|
5176
|
-
console.log(` ${highlight("pkgseer
|
|
5311
|
+
console.log(` ${highlight("pkgseer pkg info <package>", useColors)}`);
|
|
5177
5312
|
}
|
|
5178
5313
|
function registerMcpCommand(program) {
|
|
5179
5314
|
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 +5390,10 @@ async function pkgCompareAction(packages, options, deps) {
|
|
|
5255
5390
|
if (options.json) {
|
|
5256
5391
|
const pkgs = result.data.comparePackages.packages?.filter((p) => p) ?? [];
|
|
5257
5392
|
const slim = pkgs.map((p) => ({
|
|
5258
|
-
package: `${p
|
|
5259
|
-
quality: p
|
|
5260
|
-
downloads: p
|
|
5261
|
-
vulnerabilities: p
|
|
5393
|
+
package: `${p?.packageName}@${p?.version}`,
|
|
5394
|
+
quality: p?.quality?.score,
|
|
5395
|
+
downloads: p?.downloadsLastMonth,
|
|
5396
|
+
vulnerabilities: p?.vulnerabilityCount
|
|
5262
5397
|
}));
|
|
5263
5398
|
output(slim, true);
|
|
5264
5399
|
} else {
|
|
@@ -5527,8 +5662,8 @@ async function pkgQualityAction(packageArg, options, deps) {
|
|
|
5527
5662
|
score: quality?.overallScore,
|
|
5528
5663
|
grade: quality?.grade,
|
|
5529
5664
|
categories: quality?.categories?.filter((c) => c).map((c) => ({
|
|
5530
|
-
name: c
|
|
5531
|
-
score: c
|
|
5665
|
+
name: c?.category,
|
|
5666
|
+
score: c?.score
|
|
5532
5667
|
}))
|
|
5533
5668
|
};
|
|
5534
5669
|
output(slim, true);
|
|
@@ -5629,10 +5764,10 @@ async function pkgVulnsAction(packageArg, options, deps) {
|
|
|
5629
5764
|
package: `${data.package?.name}@${data.package?.version}`,
|
|
5630
5765
|
count: data.security?.vulnerabilityCount ?? 0,
|
|
5631
5766
|
vulnerabilities: vulns.filter((v) => v).map((v) => ({
|
|
5632
|
-
id: v
|
|
5633
|
-
severity: v
|
|
5634
|
-
summary: v
|
|
5635
|
-
fixed: v
|
|
5767
|
+
id: v?.osvId,
|
|
5768
|
+
severity: v?.severityScore,
|
|
5769
|
+
summary: v?.summary,
|
|
5770
|
+
fixed: v?.fixedInVersions
|
|
5636
5771
|
}))
|
|
5637
5772
|
};
|
|
5638
5773
|
output(slim, true);
|
|
@@ -5673,7 +5808,7 @@ function matchManifestsWithConfig(detectedGroups, existingManifests) {
|
|
|
5673
5808
|
});
|
|
5674
5809
|
}
|
|
5675
5810
|
}
|
|
5676
|
-
const
|
|
5811
|
+
const _labelToFiles = new Map;
|
|
5677
5812
|
const labelToConfig = new Map;
|
|
5678
5813
|
for (const detectedGroup of detectedGroups) {
|
|
5679
5814
|
for (const manifest of detectedGroup.manifests) {
|
|
@@ -5686,13 +5821,14 @@ function matchManifestsWithConfig(detectedGroups, existingManifests) {
|
|
|
5686
5821
|
label = existingConfig?.label ?? detectedGroup.label;
|
|
5687
5822
|
}
|
|
5688
5823
|
const allowMixDeps = existingConfig?.allow_mix_deps;
|
|
5689
|
-
|
|
5690
|
-
|
|
5824
|
+
let config = labelToConfig.get(label);
|
|
5825
|
+
if (!config) {
|
|
5826
|
+
config = {
|
|
5691
5827
|
files: new Set,
|
|
5692
5828
|
allow_mix_deps: allowMixDeps
|
|
5693
|
-
}
|
|
5829
|
+
};
|
|
5830
|
+
labelToConfig.set(label, config);
|
|
5694
5831
|
}
|
|
5695
|
-
const config = labelToConfig.get(label);
|
|
5696
5832
|
config.files.add(manifest.relativePath);
|
|
5697
5833
|
if (allowMixDeps) {
|
|
5698
5834
|
config.allow_mix_deps = true;
|
|
@@ -5729,7 +5865,7 @@ function matchManifestsWithConfig(detectedGroups, existingManifests) {
|
|
|
5729
5865
|
});
|
|
5730
5866
|
}
|
|
5731
5867
|
async function projectDetectAction(options, deps) {
|
|
5732
|
-
const { configService, fileSystemService, promptService
|
|
5868
|
+
const { configService, fileSystemService, promptService } = deps;
|
|
5733
5869
|
const projectConfig = await configService.loadProjectConfig();
|
|
5734
5870
|
if (!projectConfig?.config.project) {
|
|
5735
5871
|
console.error(`✗ No project is configured in pkgseer.yml`);
|
|
@@ -5786,7 +5922,7 @@ Suggested configuration:`);
|
|
|
5786
5922
|
console.log(`${prefix}${file}`);
|
|
5787
5923
|
}
|
|
5788
5924
|
}
|
|
5789
|
-
const
|
|
5925
|
+
const _hasHexManifests = detectedGroups.some((group) => group.manifests.some((m) => m.type === "hex"));
|
|
5790
5926
|
const hasHexInSuggested = suggestedManifests.some((g) => g.files.some((f) => f.endsWith("mix.exs") || f.endsWith("mix.lock")));
|
|
5791
5927
|
let allowMixDeps = false;
|
|
5792
5928
|
if (hasHexInSuggested) {
|
package/dist/index.js
CHANGED