@pkgseer/cli 0.1.5 → 0.2.1

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/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  version
4
- } from "./shared/chunk-0fnprry7.js";
4
+ } from "./shared/chunk-9016p6c6.js";
5
5
 
6
6
  // src/cli.ts
7
7
  import { Command } from "commander";
@@ -205,11 +205,65 @@ var CliDocsListDocument = gql`
205
205
  pages {
206
206
  id
207
207
  title
208
- words
209
208
  }
210
209
  }
211
210
  }
212
211
  `;
212
+ var CombinedSearchDocument = gql`
213
+ query CombinedSearch($packages: [SearchPackageInput!]!, $query: String!, $mode: SearchMode, $limit: Int) {
214
+ combinedSearch(packages: $packages, query: $query, mode: $mode, limit: $limit) {
215
+ query
216
+ mode
217
+ totalResults
218
+ entries {
219
+ id
220
+ type
221
+ title
222
+ subtitle
223
+ packageName
224
+ registry
225
+ score
226
+ snippet
227
+ filePath
228
+ startLine
229
+ endLine
230
+ language
231
+ chunkType
232
+ repoUrl
233
+ gitRef
234
+ pageId
235
+ sourceUrl
236
+ }
237
+ indexingStatus {
238
+ registry
239
+ packageName
240
+ version
241
+ codeStatus
242
+ docsStatus
243
+ }
244
+ }
245
+ }
246
+ `;
247
+ var FetchCodeContextDocument = gql`
248
+ query FetchCodeContext($repoUrl: String!, $gitRef: String!, $filePath: String!, $startLine: Int, $endLine: Int) {
249
+ fetchCodeContext(
250
+ repoUrl: $repoUrl
251
+ gitRef: $gitRef
252
+ filePath: $filePath
253
+ startLine: $startLine
254
+ endLine: $endLine
255
+ ) {
256
+ content
257
+ filePath
258
+ language
259
+ totalLines
260
+ startLine
261
+ endLine
262
+ repoUrl
263
+ gitRef
264
+ }
265
+ }
266
+ `;
213
267
  var CliDocsGetDocument = gql`
214
268
  query CliDocsGet($registry: Registry!, $packageName: String!, $pageId: String!, $version: String) {
215
269
  fetchPackageDoc(
@@ -428,7 +482,6 @@ var ListPackageDocsDocument = gql`
428
482
  slug
429
483
  order
430
484
  linkName
431
- words
432
485
  lastUpdatedAt
433
486
  sourceUrl
434
487
  }
@@ -509,7 +562,6 @@ var SearchPackageDocsDocument = gql`
509
562
  slug
510
563
  order
511
564
  linkName
512
- words
513
565
  lastUpdatedAt
514
566
  sourceUrl
515
567
  matchCount
@@ -540,7 +592,6 @@ var SearchProjectDocsDocument = gql`
540
592
  registry
541
593
  packageName
542
594
  version
543
- words
544
595
  matchCount
545
596
  titleHit
546
597
  score
@@ -560,6 +611,8 @@ var CliComparePackagesDocumentString = print(CliComparePackagesDocument);
560
611
  var CliDocsSearchDocumentString = print(CliDocsSearchDocument);
561
612
  var CliProjectDocsSearchDocumentString = print(CliProjectDocsSearchDocument);
562
613
  var CliDocsListDocumentString = print(CliDocsListDocument);
614
+ var CombinedSearchDocumentString = print(CombinedSearchDocument);
615
+ var FetchCodeContextDocumentString = print(FetchCodeContextDocument);
563
616
  var CliDocsGetDocumentString = print(CliDocsGetDocument);
564
617
  var CreateProjectDocumentString = print(CreateProjectDocument);
565
618
  var PackageSummaryDocumentString = print(PackageSummaryDocument);
@@ -598,6 +651,12 @@ function getSdk(client, withWrapper = defaultWrapper) {
598
651
  CliDocsList(variables, requestHeaders) {
599
652
  return withWrapper((wrappedRequestHeaders) => client.rawRequest(CliDocsListDocumentString, variables, { ...requestHeaders, ...wrappedRequestHeaders }), "CliDocsList", "query", variables);
600
653
  },
654
+ CombinedSearch(variables, requestHeaders) {
655
+ return withWrapper((wrappedRequestHeaders) => client.rawRequest(CombinedSearchDocumentString, variables, { ...requestHeaders, ...wrappedRequestHeaders }), "CombinedSearch", "query", variables);
656
+ },
657
+ FetchCodeContext(variables, requestHeaders) {
658
+ return withWrapper((wrappedRequestHeaders) => client.rawRequest(FetchCodeContextDocumentString, variables, { ...requestHeaders, ...wrappedRequestHeaders }), "FetchCodeContext", "query", variables);
659
+ },
601
660
  CliDocsGet(variables, requestHeaders) {
602
661
  return withWrapper((wrappedRequestHeaders) => client.rawRequest(CliDocsGetDocumentString, variables, { ...requestHeaders, ...wrappedRequestHeaders }), "CliDocsGet", "query", variables);
603
662
  },
@@ -960,7 +1019,8 @@ var TOOL_NAMES = [
960
1019
  "compare_packages",
961
1020
  "list_package_docs",
962
1021
  "fetch_package_doc",
963
- "search_package_docs",
1022
+ "search",
1023
+ "fetch_code_context",
964
1024
  "search_project_docs"
965
1025
  ];
966
1026
  var ToolNameSchema = z.enum(TOOL_NAMES);
@@ -1002,7 +1062,10 @@ class ConfigServiceImpl {
1002
1062
  return this.loadAndParseConfig(configPath, GlobalConfigSchema);
1003
1063
  }
1004
1064
  async loadProjectConfig() {
1005
- let currentDir = this.fs.getCwd();
1065
+ return this.loadProjectConfigFrom(this.fs.getCwd());
1066
+ }
1067
+ async loadProjectConfigFrom(startDir) {
1068
+ let currentDir = startDir;
1006
1069
  while (true) {
1007
1070
  const configPath = this.fs.joinPath(currentDir, PROJECT_CONFIG_FILE);
1008
1071
  const config = await this.loadAndParseConfig(configPath, ProjectConfigSchema);
@@ -1350,6 +1413,25 @@ class PkgseerServiceImpl {
1350
1413
  });
1351
1414
  return { data: result.data, errors: result.errors };
1352
1415
  }
1416
+ async combinedSearch(packages, query, options) {
1417
+ const result = await this.client.CombinedSearch({
1418
+ packages,
1419
+ query,
1420
+ mode: options?.mode,
1421
+ limit: options?.limit
1422
+ });
1423
+ return { data: result.data, errors: result.errors };
1424
+ }
1425
+ async fetchCodeContext(repoUrl, gitRef, filePath, options) {
1426
+ const result = await this.client.FetchCodeContext({
1427
+ repoUrl,
1428
+ gitRef,
1429
+ filePath,
1430
+ startLine: options?.startLine,
1431
+ endLine: options?.endLine
1432
+ });
1433
+ return { data: result.data, errors: result.errors };
1434
+ }
1353
1435
  }
1354
1436
  // src/services/project-service.ts
1355
1437
  var PROJECT_NAME_REGEX = /^[a-zA-Z0-9][a-zA-Z0-9_-]*$/;
@@ -2182,8 +2264,7 @@ function formatDocsList(docs) {
2182
2264
  for (const page of docs.pages) {
2183
2265
  if (!page || !page.id)
2184
2266
  continue;
2185
- const words = page.words ? ` (${formatNumber(page.words)} words)` : "";
2186
- lines.push(` ${page.title}${words}`);
2267
+ lines.push(` ${page.title}`);
2187
2268
  lines.push(` ID: ${page.id}`);
2188
2269
  lines.push("");
2189
2270
  }
@@ -2234,51 +2315,6 @@ function registerDocsListCommand(program) {
2234
2315
  });
2235
2316
  }
2236
2317
  // src/commands/docs/search.ts
2237
- function summarizeSearchResults(results) {
2238
- const entries = results.entries ?? [];
2239
- const count = entries.length;
2240
- const totalMatches = entries.reduce((acc, e) => acc + (e?.matchCount ?? 0), 0);
2241
- const lines = [];
2242
- lines.push(`Results: ${count} page${count === 1 ? "" : "s"} | matches: ${totalMatches}`);
2243
- lines.push("");
2244
- for (const entry of entries.filter((e) => !!e)) {
2245
- lines.push(`- ${entry.title ?? entry.slug ?? "untitled"} (${entry.matchCount ?? 0} matches)`);
2246
- }
2247
- if (count === 0) {
2248
- lines.push("No matches. Try broader terms or fewer constraints.");
2249
- }
2250
- return lines.join(`
2251
- `);
2252
- }
2253
- function buildDocRef(entry, searchResult) {
2254
- if (!entry?.slug)
2255
- return "";
2256
- let registry;
2257
- let packageName;
2258
- let version2;
2259
- if ("packageName" in entry && "registry" in entry && "version" in entry) {
2260
- registry = entry.registry;
2261
- packageName = entry.packageName;
2262
- version2 = entry.version;
2263
- } else {
2264
- const rawRegistry = "registry" in searchResult ? searchResult.registry : null;
2265
- registry = rawRegistry ? rawRegistry.toLowerCase() : null;
2266
- packageName = "packageName" in searchResult ? searchResult.packageName : null;
2267
- version2 = "version" in searchResult ? searchResult.version : null;
2268
- }
2269
- const parts = [];
2270
- if (registry)
2271
- parts.push(registry.toLowerCase());
2272
- if (packageName)
2273
- parts.push(packageName);
2274
- if (version2)
2275
- parts.push(version2);
2276
- else if (registry && packageName)
2277
- parts.push("latest");
2278
- if (entry.slug)
2279
- parts.push(entry.slug);
2280
- return parts.join("/");
2281
- }
2282
2318
  var colors = {
2283
2319
  reset: "\x1B[0m",
2284
2320
  bold: "\x1B[1m",
@@ -2286,7 +2322,8 @@ var colors = {
2286
2322
  magenta: "\x1B[35m",
2287
2323
  cyan: "\x1B[36m",
2288
2324
  yellow: "\x1B[33m",
2289
- green: "\x1B[32m"
2325
+ green: "\x1B[32m",
2326
+ blue: "\x1B[34m"
2290
2327
  };
2291
2328
  function shouldUseColors(noColor) {
2292
2329
  if (noColor)
@@ -2295,219 +2332,420 @@ function shouldUseColors(noColor) {
2295
2332
  return false;
2296
2333
  return process.stdout.isTTY ?? false;
2297
2334
  }
2298
- function applyHighlights(line, highlights, useColors) {
2299
- if (!highlights || highlights.length === 0 || !useColors) {
2300
- return line;
2301
- }
2302
- const sorted = [...highlights].filter((h) => h && h.start != null && h.end != null).sort((a, b) => {
2303
- const aStart = a?.start ?? 0;
2304
- const bStart = b?.start ?? 0;
2305
- return bStart - aStart;
2335
+ function toSearchMode(mode) {
2336
+ if (!mode)
2337
+ return "ALL";
2338
+ const map = {
2339
+ all: "ALL",
2340
+ code: "CODE",
2341
+ docs: "DOCS"
2342
+ };
2343
+ return map[mode.toLowerCase()] ?? "ALL";
2344
+ }
2345
+ function parsePackageList(input2) {
2346
+ return input2.split(",").map((spec) => {
2347
+ const trimmed = spec.trim();
2348
+ const atIndex = trimmed.lastIndexOf("@");
2349
+ let regPkg;
2350
+ let version2;
2351
+ if (atIndex > 0) {
2352
+ regPkg = trimmed.slice(0, atIndex);
2353
+ version2 = trimmed.slice(atIndex + 1);
2354
+ } else {
2355
+ regPkg = trimmed;
2356
+ }
2357
+ const slashIndex = regPkg.indexOf("/");
2358
+ if (slashIndex === -1) {
2359
+ return {
2360
+ registry: "NPM",
2361
+ name: regPkg,
2362
+ version: version2
2363
+ };
2364
+ }
2365
+ const beforeSlash = regPkg.slice(0, slashIndex);
2366
+ const afterSlash = regPkg.slice(slashIndex + 1);
2367
+ if (beforeSlash.startsWith("@")) {
2368
+ return {
2369
+ registry: "NPM",
2370
+ name: regPkg,
2371
+ version: version2
2372
+ };
2373
+ }
2374
+ return {
2375
+ registry: toGraphQLRegistry(beforeSlash),
2376
+ name: afterSlash,
2377
+ version: version2
2378
+ };
2306
2379
  });
2307
- let result = line;
2308
- for (const h of sorted) {
2309
- if (!h || h.start == null || h.end == null)
2310
- continue;
2311
- const before = result.slice(0, h.start);
2312
- const match = result.slice(h.start, h.end);
2313
- const after = result.slice(h.end);
2314
- result = `${before}${colors.bold}${colors.magenta}${match}${colors.reset}${after}`;
2380
+ }
2381
+ function formatTypeBadge(type, useColors) {
2382
+ const typeStr = type ?? "DOC";
2383
+ if (useColors) {
2384
+ const color = typeStr === "CODE" ? colors.magenta : colors.cyan;
2385
+ return `${color}[${typeStr}]${colors.reset}`;
2315
2386
  }
2316
- return result;
2387
+ return `[${typeStr}]`;
2317
2388
  }
2318
- function formatEntry(entry, searchResult, useColors) {
2389
+ function formatEntry(entry, useColors) {
2319
2390
  if (!entry)
2320
2391
  return "";
2321
2392
  const lines = [];
2322
- const ref = buildDocRef(entry, searchResult);
2323
- const matchInfo = entry.matchCount && entry.matchCount > 0 ? ` (${entry.matchCount} match${entry.matchCount > 1 ? "es" : ""})` : "";
2324
- if (useColors) {
2325
- lines.push(`${colors.cyan}${ref}${colors.reset}: ${colors.bold}${entry.title}${colors.reset}${colors.dim}${matchInfo}${colors.reset}`);
2326
- } else {
2327
- lines.push(`${ref}: ${entry.title}${matchInfo}`);
2328
- }
2329
- const matches = entry.matches ?? [];
2330
- for (const match of matches) {
2331
- if (!match?.context)
2332
- continue;
2333
- const ctx = match.context;
2334
- for (const beforeLine of ctx.before ?? []) {
2335
- if (beforeLine) {
2336
- const prefix = useColors ? colors.dim : "";
2337
- const suffix = useColors ? colors.reset : "";
2338
- lines.push(` ${prefix}${beforeLine}${suffix}`);
2393
+ const badge = formatTypeBadge(entry.type, useColors);
2394
+ if (entry.type === "CODE") {
2395
+ const title = entry.title ?? "Unknown";
2396
+ const location = entry.filePath ? `${entry.filePath}:${entry.startLine ?? "?"}-${entry.endLine ?? "?"}` : "";
2397
+ const lang = entry.language ? ` • ${entry.language}` : "";
2398
+ if (useColors) {
2399
+ lines.push(`${badge} ${colors.bold}${title}${colors.reset}`);
2400
+ if (location) {
2401
+ lines.push(` ${colors.dim}${location}${lang}${colors.reset}`);
2339
2402
  }
2340
- }
2341
- for (const matchedLine of ctx.matchedLines ?? []) {
2342
- if (matchedLine?.content) {
2343
- const highlighted = applyHighlights(matchedLine.content, matchedLine.highlights, useColors);
2344
- lines.push(` ${highlighted}`);
2403
+ } else {
2404
+ lines.push(`${badge} ${title}`);
2405
+ if (location) {
2406
+ lines.push(` ${location}${lang}`);
2345
2407
  }
2346
2408
  }
2347
- for (const afterLine of ctx.after ?? []) {
2348
- if (afterLine) {
2349
- const prefix = useColors ? colors.dim : "";
2350
- const suffix = useColors ? colors.reset : "";
2351
- lines.push(` ${prefix}${afterLine}${suffix}`);
2409
+ } else {
2410
+ const title = entry.title ?? "Untitled";
2411
+ const ref = entry.pageId ?? entry.sourceUrl ?? "";
2412
+ if (useColors) {
2413
+ lines.push(`${badge} ${colors.bold}${title}${colors.reset}`);
2414
+ if (ref) {
2415
+ lines.push(` ${colors.dim}${ref}${colors.reset}`);
2416
+ }
2417
+ } else {
2418
+ lines.push(`${badge} ${title}`);
2419
+ if (ref) {
2420
+ lines.push(` ${ref}`);
2352
2421
  }
2353
2422
  }
2423
+ }
2424
+ if (entry.snippet) {
2354
2425
  lines.push("");
2426
+ const snippetLines = entry.snippet.split(`
2427
+ `).slice(0, 5);
2428
+ for (const line of snippetLines) {
2429
+ if (useColors) {
2430
+ lines.push(` ${colors.dim}${line}${colors.reset}`);
2431
+ } else {
2432
+ lines.push(` ${line}`);
2433
+ }
2434
+ }
2355
2435
  }
2436
+ lines.push("");
2356
2437
  return lines.join(`
2357
2438
  `);
2358
2439
  }
2440
+ function formatPackageHeader(entry, prevEntry, useColors) {
2441
+ const pkg = entry?.packageName ?? "unknown";
2442
+ const registry = entry?.registry?.toLowerCase() ?? "npm";
2443
+ const prevPkg = prevEntry?.packageName;
2444
+ const prevRegistry = prevEntry?.registry?.toLowerCase();
2445
+ if (pkg === prevPkg && registry === prevRegistry) {
2446
+ return "";
2447
+ }
2448
+ const header = `${pkg} (${registry})`;
2449
+ const separator = "═".repeat(Math.min(50, header.length + 10));
2450
+ if (useColors) {
2451
+ return `
2452
+ ${colors.yellow}${header}${colors.reset}
2453
+ ${colors.dim}${separator}${colors.reset}
2454
+ `;
2455
+ }
2456
+ return `
2457
+ ${header}
2458
+ ${separator}
2459
+ `;
2460
+ }
2359
2461
  function formatSearchResults(results, useColors) {
2360
2462
  const entries = results.entries ?? [];
2361
2463
  if (entries.length === 0) {
2362
- return "No matching documentation found. Try broader terms or fewer constraints.";
2464
+ return "No results found. Try broader terms or different packages.";
2465
+ }
2466
+ const lines = [];
2467
+ let prevEntry = null;
2468
+ for (const entry of entries) {
2469
+ if (!entry)
2470
+ continue;
2471
+ const header = formatPackageHeader(entry, prevEntry, useColors);
2472
+ if (header) {
2473
+ lines.push(header);
2474
+ }
2475
+ lines.push(formatEntry(entry, useColors));
2476
+ prevEntry = entry;
2477
+ }
2478
+ const codeCount = entries.filter((e) => e?.type === "CODE").length;
2479
+ const docCount = entries.filter((e) => e?.type === "DOC" || e?.type === "REPO_DOC").length;
2480
+ if (useColors) {
2481
+ lines.push(`${colors.dim}───────────────────────────────────────────────────${colors.reset}`);
2482
+ lines.push(`${colors.dim}Results: ${entries.length} total (${codeCount} code, ${docCount} docs)${colors.reset}`);
2483
+ } else {
2484
+ lines.push("───────────────────────────────────────────────────");
2485
+ lines.push(`Results: ${entries.length} total (${codeCount} code, ${docCount} docs)`);
2486
+ }
2487
+ const indexingWarnings = formatIndexingWarnings(results.indexingStatus);
2488
+ if (indexingWarnings) {
2489
+ lines.push("");
2490
+ lines.push(indexingWarnings);
2363
2491
  }
2364
- const formatted = entries.filter((e) => e != null).map((entry) => formatEntry(entry, results, useColors));
2365
- return formatted.join(`
2492
+ return lines.join(`
2366
2493
  `);
2367
2494
  }
2368
2495
  function formatRefsOnly(results) {
2369
2496
  const entries = results.entries ?? [];
2370
- return entries.filter((e) => e != null).map((entry) => buildDocRef(entry, results)).join(`
2497
+ return entries.filter((e) => e != null).map((entry) => {
2498
+ if (!entry)
2499
+ return "";
2500
+ if (entry.type === "CODE") {
2501
+ return `${entry.repoUrl ?? ""}:${entry.filePath ?? ""}:${entry.startLine ?? ""}`;
2502
+ }
2503
+ return entry.pageId ?? entry.id ?? "";
2504
+ }).filter((s) => s !== "").join(`
2371
2505
  `);
2372
2506
  }
2373
2507
  function formatCount(results) {
2374
2508
  const entries = results.entries ?? [];
2375
- return entries.filter((e) => e != null).map((entry) => {
2509
+ const counts = new Map;
2510
+ for (const entry of entries) {
2376
2511
  if (!entry)
2377
- return "";
2378
- const ref = buildDocRef(entry, results);
2379
- return `${ref}: ${entry.matchCount ?? 0}`;
2380
- }).filter((s) => s !== "").join(`
2512
+ continue;
2513
+ const key = `${entry.packageName ?? "unknown"}@${entry.registry?.toLowerCase() ?? "npm"}`;
2514
+ const current = counts.get(key) ?? { code: 0, docs: 0 };
2515
+ if (entry.type === "CODE") {
2516
+ current.code++;
2517
+ } else {
2518
+ current.docs++;
2519
+ }
2520
+ counts.set(key, current);
2521
+ }
2522
+ return Array.from(counts.entries()).map(([pkg, { code, docs }]) => `${pkg}: ${code} code, ${docs} docs`).join(`
2523
+ `);
2524
+ }
2525
+ function formatIndexingWarnings(indexingStatus) {
2526
+ if (!indexingStatus || indexingStatus.length === 0)
2527
+ return null;
2528
+ const warnings = [];
2529
+ for (const status of indexingStatus) {
2530
+ if (!status)
2531
+ continue;
2532
+ const pkg = `${status.packageName}@${status.version ?? "latest"}`;
2533
+ if (status.codeStatus && status.codeStatus !== "INDEXED") {
2534
+ warnings.push(` - ${pkg}: code ${status.codeStatus.toLowerCase()}`);
2535
+ }
2536
+ if (status.docsStatus && status.docsStatus !== "INDEXED") {
2537
+ warnings.push(` - ${pkg}: docs ${status.docsStatus.toLowerCase()}`);
2538
+ }
2539
+ }
2540
+ if (warnings.length === 0)
2541
+ return null;
2542
+ return `Note: Some packages are not fully indexed:
2543
+ ` + warnings.join(`
2544
+ `) + `
2545
+ Results may be incomplete.`;
2546
+ }
2547
+ var LANG_ABBREV = {
2548
+ javascript: "js",
2549
+ typescript: "ts",
2550
+ python: "py",
2551
+ markdown: "md"
2552
+ };
2553
+ function abbrevLang(lang) {
2554
+ if (!lang)
2555
+ return "";
2556
+ const lower = lang.toLowerCase();
2557
+ return LANG_ABBREV[lower] ?? lower;
2558
+ }
2559
+ function truncate(text, maxLen) {
2560
+ if (text.length <= maxLen)
2561
+ return text;
2562
+ return text.slice(0, maxLen - 3) + "...";
2563
+ }
2564
+ function formatEntryCompact(entry, useColors) {
2565
+ if (!entry)
2566
+ return "";
2567
+ const badge = formatTypeBadge(entry.type, useColors);
2568
+ let header;
2569
+ if (entry.type === "CODE") {
2570
+ const loc = entry.filePath ? `${entry.filePath}:${entry.startLine ?? "?"}-${entry.endLine ?? "?"}` : "";
2571
+ const lang = abbrevLang(entry.language);
2572
+ const title = entry.title && entry.title !== "Unnamed" ? ` ${entry.title}` : "";
2573
+ header = `${badge} ${loc}${lang ? ` ${lang}` : ""}${title}`;
2574
+ } else {
2575
+ const title = entry.title ?? "Untitled";
2576
+ const ref = entry.pageId ? ` (${entry.pageId})` : "";
2577
+ header = `${badge} ${title}${ref}`;
2578
+ }
2579
+ let snippet = "";
2580
+ if (entry.snippet) {
2581
+ const firstLine = entry.snippet.split(`
2582
+ `).map((l) => l.trim()).find((l) => l.length > 0);
2583
+ if (firstLine) {
2584
+ snippet = ` ${truncate(firstLine, 80)}`;
2585
+ if (useColors) {
2586
+ snippet = ` ${colors.dim}${truncate(firstLine, 80)}${colors.reset}`;
2587
+ }
2588
+ }
2589
+ }
2590
+ return snippet ? `${header}
2591
+ ${snippet}` : header;
2592
+ }
2593
+ function formatPackageHeaderCompact(entry, prevEntry, useColors) {
2594
+ const pkg = entry?.packageName ?? "unknown";
2595
+ const registry = entry?.registry?.toLowerCase() ?? "npm";
2596
+ const prevPkg = prevEntry?.packageName;
2597
+ const prevRegistry = prevEntry?.registry?.toLowerCase();
2598
+ if (pkg === prevPkg && registry === prevRegistry) {
2599
+ return "";
2600
+ }
2601
+ const header = `${pkg} (${registry})`;
2602
+ if (useColors) {
2603
+ return `
2604
+ ${colors.yellow}${header}${colors.reset}`;
2605
+ }
2606
+ return `
2607
+ ${header}`;
2608
+ }
2609
+ function formatSearchResultsCompact(results, useColors) {
2610
+ const entries = results.entries ?? [];
2611
+ if (entries.length === 0) {
2612
+ return "No results. Try broader terms or different packages.";
2613
+ }
2614
+ const lines = [];
2615
+ let prevEntry = null;
2616
+ for (const entry of entries) {
2617
+ if (!entry)
2618
+ continue;
2619
+ const header = formatPackageHeaderCompact(entry, prevEntry, useColors);
2620
+ if (header) {
2621
+ lines.push(header);
2622
+ }
2623
+ lines.push(formatEntryCompact(entry, useColors));
2624
+ prevEntry = entry;
2625
+ }
2626
+ const codeCount = entries.filter((e) => e?.type === "CODE").length;
2627
+ const docCount = entries.filter((e) => e?.type === "DOC" || e?.type === "REPO_DOC").length;
2628
+ lines.push(`
2629
+ ${entries.length} results (${codeCount} code, ${docCount} docs)`);
2630
+ const indexingWarnings = formatIndexingWarnings(results.indexingStatus);
2631
+ if (indexingWarnings) {
2632
+ lines.push(indexingWarnings);
2633
+ }
2634
+ return lines.join(`
2381
2635
  `);
2382
2636
  }
2383
- function slimSearchResults(results) {
2637
+ function summarizeSearchResults(results) {
2384
2638
  const entries = results.entries ?? [];
2385
- return entries.filter((e) => e != null).map((entry) => {
2639
+ const codeCount = entries.filter((e) => e?.type === "CODE").length;
2640
+ const docCount = entries.filter((e) => e?.type === "DOC" || e?.type === "REPO_DOC").length;
2641
+ const lines = [];
2642
+ lines.push(`Results: ${entries.length} total | ${codeCount} code | ${docCount} docs`);
2643
+ lines.push("");
2644
+ const byPackage = new Map;
2645
+ for (const entry of entries) {
2386
2646
  if (!entry)
2387
- return null;
2388
- return {
2389
- ref: buildDocRef(entry, results),
2390
- title: entry.title ?? "",
2391
- matchCount: entry.matchCount ?? 0,
2392
- matches: entry.matches?.filter((m) => m?.context).map((m) => {
2393
- if (!m?.context)
2394
- return null;
2395
- return {
2396
- before: (m.context.before ?? []).filter((l) => l != null),
2397
- lines: (m.context.matchedLines ?? []).filter((l) => l?.content).map((l) => l?.content ?? ""),
2398
- after: (m.context.after ?? []).filter((l) => l != null)
2399
- };
2400
- }).filter((m) => m != null)
2401
- };
2402
- }).filter((e) => e != null);
2647
+ continue;
2648
+ const key = `${entry.packageName ?? "unknown"} (${entry.registry?.toLowerCase() ?? "npm"})`;
2649
+ const list = byPackage.get(key) ?? [];
2650
+ list.push(entry);
2651
+ byPackage.set(key, list);
2652
+ }
2653
+ for (const [pkg, pkgEntries] of byPackage) {
2654
+ const pkgCode = pkgEntries.filter((e) => e?.type === "CODE").length;
2655
+ const pkgDocs = pkgEntries.filter((e) => e?.type === "DOC" || e?.type === "REPO_DOC").length;
2656
+ lines.push(`- ${pkg}: ${pkgCode} code, ${pkgDocs} docs`);
2657
+ }
2658
+ if (entries.length === 0) {
2659
+ lines.push("No results. Try broader terms or different packages.");
2660
+ }
2661
+ return lines.join(`
2662
+ `);
2403
2663
  }
2404
2664
  async function docsSearchAction(queryArg, options, deps) {
2405
- const { pkgseerService, config } = deps;
2406
- const providedTerms = Array.isArray(queryArg) ? queryArg : queryArg ? [queryArg] : [];
2407
- const terms = providedTerms.concat(options.terms ?? []);
2408
- if (!terms.length) {
2409
- outputError("Search terms required. Provide terms as argument or with --terms", options.json ?? false);
2665
+ const { pkgseerService } = deps;
2666
+ const query = Array.isArray(queryArg) ? queryArg.join(" ") : queryArg ?? "";
2667
+ if (!query.trim()) {
2668
+ outputError("Search query required. Provide query as argument.", options.json ?? false);
2410
2669
  return;
2411
2670
  }
2412
- const contextBefore = options.context ? Number.parseInt(options.context, 10) : options.before ? Number.parseInt(options.before, 10) : 2;
2413
- const contextAfter = options.context ? Number.parseInt(options.context, 10) : options.after ? Number.parseInt(options.after, 10) : 2;
2414
- const maxMatches = options.maxMatches ? Number.parseInt(options.maxMatches, 10) : 5;
2415
- const limit = options.limit ? Number.parseInt(options.limit, 10) : 25;
2416
- const matchMode = options.match_mode?.toUpperCase() || options.mode?.toUpperCase() || undefined;
2417
- const useColors = shouldUseColors(options.noColor);
2418
- const project = options.project ?? config.project;
2419
- const packageName = options.package;
2420
- if (packageName) {
2421
- const registry = toGraphQLRegistry(options.registry ?? "npm");
2422
- const result = await pkgseerService.cliDocsSearch(registry, packageName, {
2423
- keywords: terms,
2424
- matchMode,
2425
- limit,
2426
- version: options.pkgVersion,
2427
- contextLinesBefore: contextBefore,
2428
- contextLinesAfter: contextAfter,
2429
- maxMatches
2430
- });
2431
- handleErrors(result.errors, options.json ?? false);
2432
- if (!result.data.searchPackageDocs) {
2433
- outputError(`No documentation found for ${packageName} in ${options.registry ?? "npm"}`, options.json ?? false);
2434
- return;
2435
- }
2436
- outputResults(result.data.searchPackageDocs, options, useColors);
2671
+ if (!options.packages) {
2672
+ outputError(`Package(s) required. Use -P or --packages:
2673
+ ` + ` -P express Single package (assumes npm)
2674
+ ` + ` -P express,lodash Multiple packages
2675
+ ` + ` -P pypi/django With registry prefix
2676
+ ` + " -P express@4.18.2 With version", options.json ?? false);
2437
2677
  return;
2438
2678
  }
2439
- if (project) {
2440
- const result = await pkgseerService.cliProjectDocsSearch(project, {
2441
- keywords: terms,
2442
- matchMode,
2443
- limit,
2444
- contextLinesBefore: contextBefore,
2445
- contextLinesAfter: contextAfter,
2446
- maxMatches
2447
- });
2448
- handleErrors(result.errors, options.json ?? false);
2449
- if (!result.data.searchProjectDocs) {
2450
- outputError(`Project not found: ${project}`, options.json ?? false);
2451
- return;
2452
- }
2453
- outputResults(result.data.searchProjectDocs, options, useColors);
2679
+ const packages = parsePackageList(options.packages);
2680
+ const limit = options.limit ? Number.parseInt(options.limit, 10) : 25;
2681
+ const mode = toSearchMode(options.mode);
2682
+ const useColors = shouldUseColors(options.noColor);
2683
+ const result = await pkgseerService.combinedSearch(packages, query, {
2684
+ mode,
2685
+ limit
2686
+ });
2687
+ handleErrors(result.errors, options.json ?? false);
2688
+ if (!result.data.combinedSearch) {
2689
+ outputError("No results returned. Check package names and registries.", options.json ?? false);
2454
2690
  return;
2455
2691
  }
2456
- outputError(`No project configured. Either:
2457
- ` + ` - Add project to pkgseer.yml
2458
- ` + ` - Use --project <name> to specify a project
2459
- ` + " - Use --package <name> to search a specific package", options.json ?? false);
2692
+ outputResults(result.data.combinedSearch, options, useColors);
2460
2693
  }
2461
2694
  function outputResults(results, options, useColors) {
2462
2695
  const format = options.json ? "json" : options.format ?? "human";
2463
2696
  if (format === "json") {
2464
- output(slimSearchResults(results), true);
2697
+ output(results, true);
2465
2698
  } else if (options.refsOnly) {
2466
2699
  console.log(formatRefsOnly(results));
2467
2700
  } else if (options.count) {
2468
2701
  console.log(formatCount(results));
2469
2702
  } else if (format === "summary") {
2470
2703
  console.log(summarizeSearchResults(results));
2471
- } else {
2704
+ } else if (options.verbose) {
2472
2705
  console.log(formatSearchResults(results, useColors));
2706
+ } else {
2707
+ console.log(formatSearchResultsCompact(results, useColors));
2473
2708
  }
2474
2709
  }
2475
- var SEARCH_DESCRIPTION = `Search documentation with grep-like output.
2476
-
2477
- Searches across package or project documentation with keyword
2478
- or freeform query support. Shows matching lines with context
2479
- and highlights matched terms.
2710
+ var SEARCH_DESCRIPTION = `Search code and documentation across packages.
2480
2711
 
2481
- By default, searches project documentation if project is
2482
- configured in pkgseer.yml. Use --package to search a specific
2483
- package instead.
2712
+ Searches both code (functions, classes) and documentation pages
2713
+ using the combined search endpoint. Returns results with snippets
2714
+ showing matches.
2484
2715
 
2485
2716
  Examples:
2486
- # Search with context (like grep)
2487
- pkgseer docs search "error handling" --package express
2488
- pkgseer docs search log --package express -C 3
2717
+ # Search a single package (code + docs)
2718
+ pkgseer docs search "error handling" -P express
2489
2719
 
2490
- # Multiple terms (OR by default)
2491
- pkgseer docs search -t "middleware,routing" --package express
2720
+ # Search code only
2721
+ pkgseer docs search "Router.use" -P express --mode code
2492
2722
 
2493
- # Strict matching (AND mode)
2494
- pkgseer docs search -t "error,middleware" --match-mode all --package express
2723
+ # Search docs only
2724
+ pkgseer docs search "middleware" -P express --mode docs
2725
+
2726
+ # Search multiple packages
2727
+ pkgseer docs search "auth" -P express,passport
2728
+
2729
+ # With registry prefix (for non-npm packages)
2730
+ pkgseer docs search "orm" -P pypi/django,pypi/sqlalchemy
2731
+
2732
+ # With specific version
2733
+ pkgseer docs search "routing" -P express@4.18.2
2495
2734
 
2496
2735
  # Output for piping
2497
- pkgseer docs search log --package express --refs-only | \\
2498
- xargs -I{} pkgseer docs get express {}
2736
+ pkgseer docs search "routing" -P express --refs-only
2499
2737
 
2500
- # Count matches
2501
- pkgseer docs search error --package express --count`;
2738
+ # JSON output
2739
+ pkgseer docs search "error" -P express --json`;
2502
2740
  function addSearchOptions(cmd) {
2503
- return cmd.option("-p, --package <name>", "Search specific package (overrides project)").option("-r, --registry <registry>", "Package registry (with --package)", "npm").option("-v, --pkg-version <version>", "Package version (with --package)").option("--project <name>", "Project name (overrides config)").option("-t, --terms <words>", "Comma-separated search terms", (val) => val.split(",").map((s) => s.trim())).option("-l, --limit <n>", "Max results (default: 25)").option("-A, --after <n>", "Lines of context after match (default: 2)").option("-B, --before <n>", "Lines of context before match (default: 2)").option("-C, --context <n>", "Lines of context before and after match").option("--max-matches <n>", "Max matches per page (default: 5)").option("--match-mode <mode>", "How to combine terms: any/or (default) or all/and").option("--refs-only", "Output only page references (for piping)").option("--count", "Output only match counts per page").option("--format <format>", "Output format: human|summary|json", "human").option("--no-color", "Disable colored output").option("--json", "Output as JSON");
2741
+ 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");
2504
2742
  }
2505
2743
  function registerDocsSearchCommand(program) {
2506
- const cmd = program.command("search [terms...]").summary("Search documentation").description(SEARCH_DESCRIPTION);
2507
- addSearchOptions(cmd).action(async (terms, options) => {
2744
+ const cmd = program.command("search [query...]").summary("Search code and documentation").description(SEARCH_DESCRIPTION);
2745
+ addSearchOptions(cmd).action(async (query, options) => {
2508
2746
  await withCliErrorHandling(options.json ?? false, async () => {
2509
2747
  const deps = await createContainer();
2510
- await docsSearchAction(terms, options, deps);
2748
+ await docsSearchAction(query, options, deps);
2511
2749
  });
2512
2750
  });
2513
2751
  }
@@ -2684,6 +2922,56 @@ Note: Create the ${dirName} directory if it doesn't exist.`, useColors));
2684
2922
  console.log(dim(`
2685
2923
  After editing, restart your AI assistant to activate the MCP server.`, useColors));
2686
2924
  }
2925
+ async function isCliAvailable(shellService, command) {
2926
+ try {
2927
+ const checkCommand = process.platform === "win32" ? `where ${command}` : `which ${command}`;
2928
+ await shellService.execute(checkCommand, process.cwd());
2929
+ return true;
2930
+ } catch {
2931
+ return false;
2932
+ }
2933
+ }
2934
+ async function setupClaudeCodeViaCli(shellService, scope) {
2935
+ try {
2936
+ const command = `claude mcp add --transport stdio --scope ${scope} pkgseer -- npx -y @pkgseer/cli mcp start`;
2937
+ await shellService.execute(command, process.cwd());
2938
+ return { success: true };
2939
+ } catch (err) {
2940
+ return {
2941
+ success: false,
2942
+ error: err instanceof Error ? err.message : String(err)
2943
+ };
2944
+ }
2945
+ }
2946
+ async function setupCodexViaCli(shellService) {
2947
+ try {
2948
+ const command = "codex mcp add pkgseer -- npx -y @pkgseer/cli mcp start";
2949
+ await shellService.execute(command, process.cwd());
2950
+ return { success: true };
2951
+ } catch (err) {
2952
+ return {
2953
+ success: false,
2954
+ error: err instanceof Error ? err.message : String(err)
2955
+ };
2956
+ }
2957
+ }
2958
+ function showAvailableTools(hasProject, useColors) {
2959
+ console.log(`
2960
+ Available MCP tools:`);
2961
+ console.log(" • package_summary - Get package overview");
2962
+ console.log(" • package_vulnerabilities - Check for security issues");
2963
+ console.log(" • package_quality - Get quality score");
2964
+ console.log(" • package_dependencies - List dependencies");
2965
+ console.log(" • compare_packages - Compare multiple packages");
2966
+ console.log(" • list_package_docs - List documentation pages");
2967
+ console.log(" • fetch_package_doc - Fetch documentation content");
2968
+ console.log(" • search_package_docs - Search package documentation");
2969
+ console.log(" • search_project_docs - Search docs across project dependencies");
2970
+ if (!hasProject) {
2971
+ console.log(dim(`
2972
+ Tip: Create pkgseer.yml to enable automatic project detection for search_project_docs.`, useColors));
2973
+ }
2974
+ }
2687
2975
  async function mcpInitAction(deps) {
2688
2976
  const { fileSystemService: fs, promptService, hasProject } = deps;
2689
2977
  const useColors = shouldUseColors2();
@@ -2736,47 +3024,84 @@ Run ${highlight("pkgseer project init", useColors)} first, then ${highlight("pkg
2736
3024
  if (tool === "cursor" || tool === "codex" || tool === "claude-code") {
2737
3025
  const projectPath = tool === "cursor" ? ".cursor/mcp.json" : tool === "codex" ? ".codex/config.toml" : ".mcp.json";
2738
3026
  const globalPath = tool === "cursor" ? "~/.cursor/mcp.json" : tool === "codex" ? "~/.codex/config.toml" : "~/.claude.json";
2739
- if (hasProject) {
2740
- scope = await promptService.select("Where should the MCP config be created?", [
3027
+ if (tool === "codex") {
3028
+ console.log(dim(`
3029
+ Codex uses user-level configuration.
3030
+ `, useColors));
3031
+ scope = "global";
3032
+ } else {
3033
+ scope = await promptService.select("Where should the MCP server be installed?", [
2741
3034
  {
2742
- value: "project",
2743
- name: `Project (${projectPath}) – recommended`,
2744
- description: "Uses project token; enables docs search for your packages"
3035
+ value: "global",
3036
+ name: `User-level (${globalPath}) – recommended`,
3037
+ description: "Works across all projects. Use project_directory param for project features."
2745
3038
  },
2746
3039
  {
2747
- value: "global",
2748
- name: `Global (${globalPath})`,
2749
- description: "Works everywhere but without project-specific features"
3040
+ value: "project",
3041
+ name: `Project-level (${projectPath})`,
3042
+ description: "Scoped to this project only."
2750
3043
  }
2751
3044
  ]);
2752
- } else {
2753
- console.log(dim(`
2754
- Using global config (no pkgseer.yml found).
2755
- `, useColors));
2756
- scope = "global";
2757
3045
  }
2758
3046
  }
2759
3047
  let configPath;
2760
3048
  let backupPath;
2761
- if (tool === "cursor") {
3049
+ const { shellService } = deps;
3050
+ if (tool === "claude-code") {
2762
3051
  if (!scope) {
2763
3052
  scope = "global";
2764
3053
  }
2765
- const paths = getCursorConfigPaths(fs, scope);
3054
+ const claudeAvailable = await isCliAvailable(shellService, "claude");
3055
+ if (claudeAvailable) {
3056
+ console.log(dim(`
3057
+ Using Claude Code CLI to add MCP server...
3058
+ `, useColors));
3059
+ const cliScope = scope === "global" ? "user" : "project";
3060
+ const result = await setupClaudeCodeViaCli(shellService, cliScope);
3061
+ if (result.success) {
3062
+ console.log(success("PkgSeer MCP server configured for Claude Code!", useColors));
3063
+ console.log(dim(`
3064
+ The MCP server has been registered. It should be available immediately.`, useColors));
3065
+ showAvailableTools(hasProject, useColors);
3066
+ return;
3067
+ }
3068
+ console.log(dim(`
3069
+ CLI setup failed: ${result.error}
3070
+ Falling back to file editing...
3071
+ `, useColors));
3072
+ }
3073
+ const paths = getClaudeCodeConfigPaths(fs, scope);
2766
3074
  configPath = paths.configPath;
2767
3075
  backupPath = paths.backupPath;
2768
3076
  } else if (tool === "codex") {
2769
3077
  if (!scope) {
2770
- scope = hasProject ? "project" : "global";
3078
+ scope = "global";
3079
+ }
3080
+ const codexAvailable = await isCliAvailable(shellService, "codex");
3081
+ if (codexAvailable) {
3082
+ console.log(dim(`
3083
+ Using Codex CLI to add MCP server...
3084
+ `, useColors));
3085
+ const result = await setupCodexViaCli(shellService);
3086
+ if (result.success) {
3087
+ console.log(success("PkgSeer MCP server configured for Codex CLI!", useColors));
3088
+ console.log(dim(`
3089
+ The MCP server has been registered. It should be available immediately.`, useColors));
3090
+ showAvailableTools(hasProject, useColors);
3091
+ return;
3092
+ }
3093
+ console.log(dim(`
3094
+ CLI setup failed: ${result.error}
3095
+ `, useColors));
2771
3096
  }
2772
3097
  const paths = getCodexConfigPaths(fs, scope);
2773
3098
  showManualInstructions("codex", scope, paths.configPath, useColors);
2774
3099
  return;
2775
- } else if (tool === "claude-code") {
3100
+ } else if (tool === "cursor") {
2776
3101
  if (!scope) {
2777
3102
  scope = "global";
2778
3103
  }
2779
- const paths = getClaudeCodeConfigPaths(fs, scope);
3104
+ const paths = getCursorConfigPaths(fs, scope);
2780
3105
  configPath = paths.configPath;
2781
3106
  backupPath = paths.backupPath;
2782
3107
  } else {
@@ -2853,31 +3178,19 @@ Config file: ${highlight(configPath, useColors)}`);
2853
3178
  Next steps:
2854
3179
  1. Restart your AI assistant to activate the MCP server
2855
3180
  2. Test by asking your assistant about packages`, useColors));
2856
- console.log(`
2857
- Available MCP tools:`);
2858
- console.log(" • package_summary - Get package overview");
2859
- console.log(" • package_vulnerabilities - Check for security issues");
2860
- console.log(" • package_quality - Get quality score");
2861
- console.log(" • package_dependencies - List dependencies");
2862
- console.log(" • compare_packages - Compare multiple packages");
2863
- console.log(" • list_package_docs - List documentation pages");
2864
- console.log(" • fetch_package_doc - Fetch documentation content");
2865
- console.log(" • search_package_docs - Search package documentation");
2866
- if (hasProject) {
2867
- console.log(" • search_project_docs - Search your project's docs");
2868
- }
3181
+ showAvailableTools(hasProject, useColors);
2869
3182
  }
2870
3183
  var MCP_INIT_DESCRIPTION = `Configure PkgSeer's MCP server for your AI assistant.
2871
3184
 
2872
3185
  Guides you through:
2873
3186
  • Selecting your AI tool (Cursor IDE, Codex CLI, Claude Code)
2874
- • Choosing configuration location (project or global)
2875
- Safely editing config files with automatic backup
3187
+ • Choosing installation scope (user-level or project-level)
3188
+ Setting up via CLI commands or config file editing
2876
3189
 
2877
- Project-level config (recommended):
2878
- Uses your project token for authenticated features
2879
- Enables search_project_docs (search your project's packages)
2880
- Portable with your project`;
3190
+ User-level (recommended):
3191
+ Works across all your projects
3192
+ Use project_directory parameter for project-specific features
3193
+ Automatically updated when using npx`;
2881
3194
  function registerMcpInitCommand(mcpCommand) {
2882
3195
  mcpCommand.command("init").summary("Configure MCP server for AI assistants").description(MCP_INIT_DESCRIPTION).action(async () => {
2883
3196
  const deps = await createContainer();
@@ -2886,6 +3199,7 @@ function registerMcpInitCommand(mcpCommand) {
2886
3199
  fileSystemService: deps.fileSystemService,
2887
3200
  promptService: deps.promptService,
2888
3201
  configService: deps.configService,
3202
+ shellService: deps.shellService,
2889
3203
  baseUrl: deps.baseUrl,
2890
3204
  hasProject
2891
3205
  });
@@ -3525,30 +3839,362 @@ function registerProjectInitCommand(program) {
3525
3839
  });
3526
3840
  }
3527
3841
 
3528
- // src/commands/init.ts
3529
- async function initAction(options, deps) {
3530
- const useColors = shouldUseColors2();
3531
- console.log("Welcome to PkgSeer!");
3532
- console.log(`───────────────────
3533
- `);
3534
- console.log(`Set up PkgSeer for your project:
3842
+ // src/commands/skill-init.ts
3843
+ function generateSkillContent() {
3844
+ return `---
3845
+ name: pkgseer
3846
+ version: ${version}
3847
+ description: >-
3848
+ Search code and documentation across npm, PyPI, and Hex packages.
3849
+ Find functions, classes, APIs, and usage examples. Also provides
3850
+ quality scores, security vulnerabilities, dependencies, and comparisons.
3851
+ Triggers on: "how does X work", "find examples of", "search for",
3852
+ "is X secure", "compare X vs Y", package evaluation, dependency decisions,
3853
+ "what package should I use for", API lookup, security audits.
3854
+ ---
3855
+
3856
+ # PkgSeer - Package Intelligence
3857
+
3858
+ Search and analyze packages across npm, PyPI, and Hex. All commands support \`--json\`.
3859
+
3860
+ ## Search (Primary Use Case)
3861
+
3862
+ \`\`\`bash
3863
+ # Search code and docs across packages
3864
+ pkgseer search "<query>" -P lodash,express # npm packages
3865
+ pkgseer search "<query>" -P requests -r pypi # PyPI packages
3866
+ pkgseer search "authentication" -P phoenix,plug -r hex
3867
+
3868
+ # Search modes
3869
+ pkgseer search "<query>" -P <packages> --code # Code only
3870
+ pkgseer search "<query>" -P <packages> --docs # Docs only
3871
+
3872
+ # Search project dependencies (requires pkgseer.yml)
3873
+ pkgseer docs search "<query>"
3874
+ \`\`\`
3875
+
3876
+ ## Package Analysis
3877
+
3878
+ \`\`\`bash
3879
+ # Overview: metadata, versions, quickstart
3880
+ pkgseer pkg info <package> [-r npm|pypi|hex]
3881
+
3882
+ # Quality score (0-100) with category breakdown
3883
+ pkgseer pkg quality <package> [-r registry] [-v version]
3884
+
3885
+ # Security: CVEs, severity, upgrade paths
3886
+ pkgseer pkg vulns <package> [-r registry] [-v version]
3887
+
3888
+ # Dependencies: direct, transitive, tree view
3889
+ pkgseer pkg deps <package> [-r registry] [-t] [-d depth]
3890
+
3891
+ # Compare up to 10 packages
3892
+ pkgseer pkg compare lodash underscore ramda
3893
+ pkgseer pkg compare npm:axios pypi:httpx # cross-registry
3894
+ \`\`\`
3895
+
3896
+ ## Documentation
3897
+
3898
+ \`\`\`bash
3899
+ pkgseer docs list <package> [-r registry] # List pages
3900
+ pkgseer docs get <package>/<page-id> # Fetch content
3901
+ \`\`\`
3902
+
3903
+ ## Tips
3904
+
3905
+ - Default registry: npm. Use \`-r pypi\` or \`-r hex\` for others
3906
+ - Use \`--json\` for structured output when parsing
3907
+ - Version defaults to latest; use \`-v\` for specific
3908
+ `;
3909
+ }
3910
+ function extractSkillVersion(content) {
3911
+ const match = content.match(/^version:\s*["']?([^"'\n]+)["']?/m);
3912
+ return match?.[1]?.trim() ?? null;
3913
+ }
3914
+ function getClaudeCodeSkillPaths(fs, scope) {
3915
+ const baseDir = scope === "user" ? fs.joinPath(fs.getHomeDir(), ".claude", "skills", "pkgseer") : fs.joinPath(fs.getCwd(), ".claude", "skills", "pkgseer");
3916
+ return {
3917
+ skillDir: baseDir,
3918
+ skillPath: fs.joinPath(baseDir, "SKILL.md")
3919
+ };
3920
+ }
3921
+ function getCodexSkillPaths(fs, scope) {
3922
+ const baseDir = scope === "user" ? fs.joinPath(fs.getHomeDir(), ".codex", "skills", "pkgseer") : fs.joinPath(fs.getCwd(), ".codex", "skills", "pkgseer");
3923
+ return {
3924
+ skillDir: baseDir,
3925
+ skillPath: fs.joinPath(baseDir, "SKILL.md")
3926
+ };
3927
+ }
3928
+ async function installSkill(fs, skillDir, skillPath) {
3929
+ try {
3930
+ const exists = await fs.exists(skillPath);
3931
+ let updated = false;
3932
+ if (exists) {
3933
+ const existingContent = await fs.readFile(skillPath);
3934
+ const existingVersion = extractSkillVersion(existingContent);
3935
+ if (existingVersion === version) {
3936
+ return {
3937
+ success: true,
3938
+ path: skillPath,
3939
+ updated: false
3940
+ };
3941
+ }
3942
+ updated = true;
3943
+ }
3944
+ await fs.ensureDir(skillDir);
3945
+ const content = generateSkillContent();
3946
+ await fs.writeFile(skillPath, content);
3947
+ return {
3948
+ success: true,
3949
+ path: skillPath,
3950
+ updated
3951
+ };
3952
+ } catch (err) {
3953
+ return {
3954
+ success: false,
3955
+ error: err instanceof Error ? err.message : String(err)
3956
+ };
3957
+ }
3958
+ }
3959
+ async function skillInitAction(deps) {
3960
+ const { fileSystemService: fs, promptService } = deps;
3961
+ const useColors = shouldUseColors2();
3962
+ console.log("Skill Setup");
3963
+ console.log(`───────────
3964
+ `);
3965
+ console.log(`Install PkgSeer as an agent skill for your AI assistant.
3966
+ `);
3967
+ console.log(dim(`Skills provide Claude/Codex with package intelligence capabilities
3968
+ ` + `through natural language - no MCP server required.
3969
+ `, useColors));
3970
+ const tool = await promptService.select("Which AI tool would you like to configure?", [
3971
+ {
3972
+ value: "claude-code",
3973
+ name: "Claude Code",
3974
+ description: "Install skill for Claude Code CLI"
3975
+ },
3976
+ {
3977
+ value: "codex",
3978
+ name: "Codex CLI",
3979
+ description: "Install skill for OpenAI Codex CLI"
3980
+ },
3981
+ {
3982
+ value: "other",
3983
+ name: "Other / Manual",
3984
+ description: "Show SKILL.md content for manual installation"
3985
+ }
3986
+ ]);
3987
+ if (tool === "other") {
3988
+ showManualInstructions2(useColors);
3989
+ return;
3990
+ }
3991
+ const scope = await promptService.select("Where should the skill be installed?", [
3992
+ {
3993
+ value: "user",
3994
+ name: "User-level (recommended)",
3995
+ description: "Available in all your projects"
3996
+ },
3997
+ {
3998
+ value: "project",
3999
+ name: "Project-level",
4000
+ description: "Only available in this project"
4001
+ }
4002
+ ]);
4003
+ const paths = tool === "claude-code" ? getClaudeCodeSkillPaths(fs, scope) : getCodexSkillPaths(fs, scope);
4004
+ const exists = await fs.exists(paths.skillPath);
4005
+ if (exists) {
4006
+ const existingContent = await fs.readFile(paths.skillPath);
4007
+ const existingVersion = extractSkillVersion(existingContent);
4008
+ if (existingVersion === version) {
4009
+ console.log(success(`
4010
+ PkgSeer skill already installed (v${version})`, useColors));
4011
+ console.log(`Location: ${highlight(paths.skillPath, useColors)}`);
4012
+ return;
4013
+ }
4014
+ console.log(`
4015
+ Existing skill found: v${existingVersion ?? "unknown"} → v${version}`);
4016
+ const proceed = await promptService.confirm("Update to latest version?", true);
4017
+ if (!proceed) {
4018
+ console.log(dim(`
4019
+ Skip update.`, useColors));
4020
+ return;
4021
+ }
4022
+ }
4023
+ console.log(dim(`
4024
+ Installing skill...`, useColors));
4025
+ const result = await installSkill(fs, paths.skillDir, paths.skillPath);
4026
+ if (!result.success) {
4027
+ console.log(error(`
4028
+ Failed to install skill: ${result.error}`, useColors));
4029
+ showManualInstructions2(useColors);
4030
+ return;
4031
+ }
4032
+ const toolName = tool === "claude-code" ? "Claude Code" : "Codex";
4033
+ const action = result.updated ? "updated" : "installed";
4034
+ console.log(success(`
4035
+ PkgSeer skill ${action} for ${toolName}!`, useColors));
4036
+ console.log(`Location: ${highlight(paths.skillPath, useColors)}`);
4037
+ console.log(dim(`
4038
+ The skill is now available. Try asking:
4039
+ ` + ` "Search for authentication examples in express"
4040
+ ` + ` "Is lodash secure? Check for vulnerabilities"
4041
+ ` + ' "Compare axios vs fetch vs got"', useColors));
4042
+ }
4043
+ function showManualInstructions2(useColors) {
4044
+ console.log(`
4045
+ Manual Installation`);
4046
+ console.log(`───────────────────
4047
+ `);
4048
+ console.log(`Create a SKILL.md file in your skills directory:
4049
+ `);
4050
+ console.log("Claude Code:");
4051
+ console.log(` User: ${highlight("~/.claude/skills/pkgseer/SKILL.md", useColors)}`);
4052
+ console.log(` Project: ${highlight(".claude/skills/pkgseer/SKILL.md", useColors)}
4053
+ `);
4054
+ console.log("Codex:");
4055
+ console.log(` User: ${highlight("~/.codex/skills/pkgseer/SKILL.md", useColors)}`);
4056
+ console.log(` Project: ${highlight(".codex/skills/pkgseer/SKILL.md", useColors)}
4057
+ `);
4058
+ console.log(`Content:
4059
+ `);
4060
+ console.log("─".repeat(60));
4061
+ console.log(generateSkillContent());
4062
+ console.log("─".repeat(60));
4063
+ }
4064
+ async function skillUpdateAction(deps) {
4065
+ const { fileSystemService: fs } = deps;
4066
+ const useColors = shouldUseColors2();
4067
+ console.log(`Checking for skill updates...
4068
+ `);
4069
+ const locations = [
4070
+ {
4071
+ name: "Claude Code (user)",
4072
+ ...getClaudeCodeSkillPaths(fs, "user")
4073
+ },
4074
+ {
4075
+ name: "Claude Code (project)",
4076
+ ...getClaudeCodeSkillPaths(fs, "project")
4077
+ },
4078
+ {
4079
+ name: "Codex (user)",
4080
+ ...getCodexSkillPaths(fs, "user")
4081
+ },
4082
+ {
4083
+ name: "Codex (project)",
4084
+ ...getCodexSkillPaths(fs, "project")
4085
+ }
4086
+ ];
4087
+ let foundAny = false;
4088
+ let updatedAny = false;
4089
+ for (const loc of locations) {
4090
+ const exists = await fs.exists(loc.skillPath);
4091
+ if (!exists)
4092
+ continue;
4093
+ foundAny = true;
4094
+ const content = await fs.readFile(loc.skillPath);
4095
+ const existingVersion = extractSkillVersion(content);
4096
+ if (existingVersion === version) {
4097
+ console.log(`${loc.name}: ${highlight(`v${version}`, useColors)} (current)`);
4098
+ continue;
4099
+ }
4100
+ const result = await installSkill(fs, loc.skillDir, loc.skillPath);
4101
+ if (result.success) {
4102
+ console.log(`${loc.name}: ${dim(`v${existingVersion ?? "unknown"}`, useColors)} → ${highlight(`v${version}`, useColors)}`);
4103
+ updatedAny = true;
4104
+ } else {
4105
+ console.log(error(`${loc.name}: Failed to update - ${result.error}`, useColors));
4106
+ }
4107
+ }
4108
+ if (!foundAny) {
4109
+ console.log(dim("No installed skills found.", useColors));
4110
+ console.log(dim(`Run 'pkgseer skill init' to install the skill.
4111
+ `, useColors));
4112
+ return;
4113
+ }
4114
+ if (updatedAny) {
4115
+ console.log(success(`
4116
+ Skills updated successfully!`, useColors));
4117
+ } else {
4118
+ console.log(dim(`
4119
+ All skills are up to date.`, useColors));
4120
+ }
4121
+ }
4122
+ var SKILL_INIT_DESCRIPTION = `Install PkgSeer as an agent skill for AI assistants.
4123
+
4124
+ Skills provide package intelligence through natural language:
4125
+ • Search code and documentation across packages
4126
+ • Check security vulnerabilities and quality scores
4127
+ • Compare packages and analyze dependencies
4128
+
4129
+ Supports Claude Code and Codex CLI with user or project scope.`;
4130
+ var SKILL_UPDATE_DESCRIPTION = `Update installed PkgSeer skills to the latest version.
4131
+
4132
+ Checks all installed skill locations and updates any that are outdated.`;
4133
+ function registerSkillCommand(program) {
4134
+ const skillCmd = program.command("skill").summary("Manage PkgSeer agent skill").description("Install and manage PkgSeer as an agent skill for AI assistants.");
4135
+ skillCmd.command("init").summary("Install skill for AI assistants").description(SKILL_INIT_DESCRIPTION).action(async () => {
4136
+ const deps = await createContainer();
4137
+ await skillInitAction({
4138
+ fileSystemService: deps.fileSystemService,
4139
+ promptService: deps.promptService,
4140
+ shellService: deps.shellService
4141
+ });
4142
+ });
4143
+ skillCmd.command("update").summary("Update installed skills").description(SKILL_UPDATE_DESCRIPTION).action(async () => {
4144
+ const deps = await createContainer();
4145
+ await skillUpdateAction({
4146
+ fileSystemService: deps.fileSystemService,
4147
+ promptService: deps.promptService,
4148
+ shellService: deps.shellService
4149
+ });
4150
+ });
4151
+ }
4152
+
4153
+ // src/commands/init.ts
4154
+ async function initAction(options, deps) {
4155
+ const useColors = shouldUseColors2();
4156
+ console.log("Welcome to PkgSeer!");
4157
+ console.log(`───────────────────
4158
+ `);
4159
+ console.log(`Set up PkgSeer for your project:
3535
4160
  ` + ` 1. Project configuration – track dependencies and monitor vulnerabilities
3536
- ` + ` 2. MCP server – enable AI assistant integration
4161
+ ` + ` 2. AI integration – enable AI assistant capabilities
3537
4162
  `);
3538
- console.log(dim(`Or run separately: pkgseer project init, pkgseer mcp init
4163
+ console.log(dim(`Or run separately: pkgseer project init, pkgseer skill init, pkgseer mcp init
3539
4164
  `, useColors));
3540
4165
  let setupProject = !options.skipProject;
3541
- let setupMcp = !options.skipMcp;
3542
- if (options.skipProject && options.skipMcp) {
4166
+ let aiIntegration = "none";
4167
+ if (options.skipProject && options.skipMcp && options.skipSkill) {
3543
4168
  showCliUsage(useColors);
3544
4169
  return;
3545
4170
  }
3546
- if (!options.skipProject && !options.skipMcp) {
3547
- const choice = await deps.promptService.select("What would you like to set up?", [
4171
+ const promptForAiIntegration = async () => {
4172
+ return deps.promptService.select("Which AI integration would you like?", [
4173
+ {
4174
+ value: "skill",
4175
+ name: "Skill (recommended for Claude Code / Codex)",
4176
+ description: "Lightweight file-based integration, auto-triggers on package queries"
4177
+ },
4178
+ {
4179
+ value: "mcp",
4180
+ name: "MCP server (for Cursor / advanced use)",
4181
+ description: "Structured tool integration via Model Context Protocol"
4182
+ }
4183
+ ]);
4184
+ };
4185
+ if (options.skipMcp && !options.skipSkill) {
4186
+ aiIntegration = "skill";
4187
+ } else if (!options.skipMcp && options.skipSkill) {
4188
+ aiIntegration = "mcp";
4189
+ } else if (options.skipMcp && options.skipSkill) {
4190
+ aiIntegration = "none";
4191
+ }
4192
+ if (!options.skipProject && !options.skipMcp && !options.skipSkill) {
4193
+ const setupChoice = await deps.promptService.select("What would you like to set up?", [
3548
4194
  {
3549
- value: "both",
3550
- name: "Both project and MCP server (recommended)",
3551
- description: "Full setup: dependency tracking + AI assistant integration"
4195
+ value: "full",
4196
+ name: "Project + AI integration (recommended)",
4197
+ description: "Full setup: dependency tracking + AI assistant capabilities"
3552
4198
  },
3553
4199
  {
3554
4200
  value: "project",
@@ -3556,9 +4202,9 @@ async function initAction(options, deps) {
3556
4202
  description: "Track dependencies and monitor for vulnerabilities"
3557
4203
  },
3558
4204
  {
3559
- value: "mcp",
3560
- name: "MCP server only",
3561
- description: "Enable AI assistant integration"
4205
+ value: "ai",
4206
+ name: "AI integration only",
4207
+ description: "Enable AI assistant capabilities"
3562
4208
  },
3563
4209
  {
3564
4210
  value: "cli",
@@ -3566,15 +4212,21 @@ async function initAction(options, deps) {
3566
4212
  description: "Use PkgSeer as a command-line tool"
3567
4213
  }
3568
4214
  ]);
3569
- if (choice === "cli") {
4215
+ if (setupChoice === "cli") {
3570
4216
  showCliUsage(useColors);
3571
4217
  return;
3572
4218
  }
3573
- setupProject = choice === "both" || choice === "project";
3574
- setupMcp = choice === "both" || choice === "mcp";
4219
+ setupProject = setupChoice === "full" || setupChoice === "project";
4220
+ const setupAi = setupChoice === "full" || setupChoice === "ai";
4221
+ if (setupAi) {
4222
+ aiIntegration = await promptForAiIntegration();
4223
+ }
4224
+ } else if (!options.skipMcp && !options.skipSkill) {
4225
+ aiIntegration = await promptForAiIntegration();
3575
4226
  }
3576
4227
  const projectInit = deps.projectInitAction ?? projectInitAction;
3577
4228
  const mcpInit = deps.mcpInitAction ?? mcpInitAction;
4229
+ const skillInit = deps.skillInitAction ?? skillInitAction;
3578
4230
  if (setupProject) {
3579
4231
  const existingConfig = await deps.configService.loadProjectConfig();
3580
4232
  if (existingConfig?.config.project) {
@@ -3606,7 +4258,21 @@ ${highlight("✓", useColors)} Project setup complete!
3606
4258
  }
3607
4259
  }
3608
4260
  }
3609
- if (setupMcp) {
4261
+ if (aiIntegration === "skill") {
4262
+ console.log(`
4263
+ ` + "=".repeat(50));
4264
+ console.log("Skill Setup");
4265
+ console.log("=".repeat(50) + `
4266
+ `);
4267
+ await skillInit({
4268
+ fileSystemService: deps.fileSystemService,
4269
+ promptService: deps.promptService,
4270
+ shellService: deps.shellService
4271
+ });
4272
+ console.log(`
4273
+ ${highlight("✓", useColors)} Skill setup complete!
4274
+ `);
4275
+ } else if (aiIntegration === "mcp") {
3610
4276
  const currentConfig = await deps.configService.loadProjectConfig();
3611
4277
  const hasProjectNow = currentConfig?.config.project !== undefined;
3612
4278
  console.log(`
@@ -3618,6 +4284,7 @@ ${highlight("✓", useColors)} Project setup complete!
3618
4284
  fileSystemService: deps.fileSystemService,
3619
4285
  promptService: deps.promptService,
3620
4286
  configService: deps.configService,
4287
+ shellService: deps.shellService,
3621
4288
  baseUrl: deps.baseUrl,
3622
4289
  hasProject: hasProjectNow
3623
4290
  });
@@ -3636,15 +4303,18 @@ ${highlight("✓", useColors)} MCP setup complete!
3636
4303
  console.log(dim(` View at: ${deps.baseUrl}/projects/${finalConfig.config.project}`, useColors));
3637
4304
  }
3638
4305
  }
3639
- if (setupMcp) {
3640
- console.log("MCP Server: Configured");
4306
+ if (aiIntegration === "skill") {
4307
+ console.log("AI Integration: Skill installed");
4308
+ console.log(dim(" The skill is available immediately.", useColors));
4309
+ } else if (aiIntegration === "mcp") {
4310
+ console.log("AI Integration: MCP server configured");
3641
4311
  console.log(dim(" Restart your AI assistant to activate the MCP server.", useColors));
3642
4312
  }
3643
4313
  console.log(dim(`
3644
4314
  Next steps:
3645
4315
  ` + ` • Use CLI commands: pkgseer pkg info <package>
3646
- ` + ` • Search docs: pkgseer docs search <query>
3647
- ` + " • Quick reference: pkgseer quickstart", useColors));
4316
+ ` + ` • Search packages: pkgseer search <query> -P <packages>
4317
+ ` + " • Search project docs: pkgseer docs search <query>", useColors));
3648
4318
  }
3649
4319
  function showCliUsage(useColors) {
3650
4320
  console.log("Using PkgSeer via CLI");
@@ -3669,13 +4339,13 @@ function showCliUsage(useColors) {
3669
4339
  ` + "Tip: Run 'pkgseer quickstart' for a quick reference guide.", useColors));
3670
4340
  }
3671
4341
  function registerInitCommand(program) {
3672
- program.command("init").summary("Set up project and MCP server").description(`Set up PkgSeer for your project.
4342
+ program.command("init").summary("Set up project and AI integration").description(`Set up PkgSeer for your project.
3673
4343
 
3674
4344
  Guides you through:
3675
4345
  • Project configuration – track dependencies, monitor vulnerabilities
3676
- • MCP server enable AI assistant integration
4346
+ AI integration – skill or MCP server for AI assistants
3677
4347
 
3678
- Run separately: pkgseer project init, pkgseer mcp init`).option("--skip-project", "Skip project setup").option("--skip-mcp", "Skip MCP setup").action(async (options) => {
4348
+ Run separately: pkgseer project init, pkgseer skill init, pkgseer mcp init`).option("--skip-project", "Skip project setup").option("--skip-mcp", "Skip MCP setup (use skill instead)").option("--skip-skill", "Skip skill setup (use MCP instead)").action(async (options) => {
3679
4349
  const deps = await createContainer();
3680
4350
  await initAction(options, deps);
3681
4351
  });
@@ -3912,7 +4582,7 @@ var argsSchema = {
3912
4582
  function createComparePackagesTool(pkgseerService) {
3913
4583
  return {
3914
4584
  name: "compare_packages",
3915
- description: 'Compares 2-10 packages across metadata, quality, and security. Example: [{"registry":"npm","name":"react","version":"18.2.0"},{"registry":"pypi","name":"requests"}].',
4585
+ 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"}]',
3916
4586
  schema: argsSchema,
3917
4587
  handler: async ({ packages }, _extra) => {
3918
4588
  return withErrorHandling("compare packages", async () => {
@@ -3933,16 +4603,47 @@ function createComparePackagesTool(pkgseerService) {
3933
4603
  }
3934
4604
  };
3935
4605
  }
3936
- // src/tools/fetch-package-doc.ts
4606
+ // src/tools/fetch-code-context.ts
3937
4607
  import { z as z4 } from "zod";
3938
4608
  var argsSchema2 = {
3939
- page_id: z4.string().max(500).describe("Globally unique documentation page identifier (from list_package_docs)")
4609
+ repo_url: z4.string().min(1).describe("Repository URL (GitHub). Example: https://github.com/expressjs/express"),
4610
+ git_ref: z4.string().min(1).describe("Git reference (tag, commit, or branch). Example: v4.18.2"),
4611
+ file_path: z4.string().min(1).describe("Path to file in repository. Example: src/router/index.js"),
4612
+ start_line: z4.number().int().positive().optional().describe("Starting line (1-indexed). If omitted, starts from line 1."),
4613
+ end_line: z4.number().int().positive().optional().describe("Ending line. If omitted, returns to end of file.")
4614
+ };
4615
+ function createFetchCodeContextTool(pkgseerService) {
4616
+ return {
4617
+ name: "fetch_code_context",
4618
+ description: "Fetch code content from a repository. Use this to expand code search results and see " + "more context around a match. Returns full file by default, or a specific line range. " + "Requires repo_url, git_ref (tag/commit/branch), and file_path from search results.",
4619
+ schema: argsSchema2,
4620
+ handler: async ({ repo_url, git_ref, file_path, start_line, end_line }, _extra) => {
4621
+ return withErrorHandling("fetch code context", async () => {
4622
+ const result = await pkgseerService.fetchCodeContext(repo_url, git_ref, file_path, {
4623
+ startLine: start_line,
4624
+ endLine: end_line
4625
+ });
4626
+ const graphqlError = handleGraphQLErrors(result.errors);
4627
+ if (graphqlError)
4628
+ return graphqlError;
4629
+ if (!result.data.fetchCodeContext) {
4630
+ return errorResult(`Code not found: ${file_path} at ${git_ref} in ${repo_url}. ` + "Check that the repository, file path, and git ref are correct.");
4631
+ }
4632
+ return textResult(JSON.stringify(result.data.fetchCodeContext, null, 2));
4633
+ });
4634
+ }
4635
+ };
4636
+ }
4637
+ // src/tools/fetch-package-doc.ts
4638
+ import { z as z5 } from "zod";
4639
+ var argsSchema3 = {
4640
+ page_id: z5.string().max(500).describe("Globally unique documentation page identifier (from list_package_docs)")
3940
4641
  };
3941
4642
  function createFetchPackageDocTool(pkgseerService) {
3942
4643
  return {
3943
4644
  name: "fetch_package_doc",
3944
- description: "Fetches the full content of a documentation page by globally unique page ID (from list_package_docs). Returns full metadata (title, content, format, breadcrumbs, source, base URL, last updated).",
3945
- schema: argsSchema2,
4645
+ description: "Get full content of a documentation page. Requires page_id from list_package_docs. " + "Returns: title, full content (markdown/HTML), format type, navigation breadcrumbs, " + "source URL, and last updated timestamp. Use this when you need complete documentation, " + "not just search snippets.",
4646
+ schema: argsSchema3,
3946
4647
  handler: async ({ page_id }, _extra) => {
3947
4648
  return withErrorHandling("fetch documentation page", async () => {
3948
4649
  const result = await pkgseerService.getDocPage(page_id);
@@ -3958,7 +4659,7 @@ function createFetchPackageDocTool(pkgseerService) {
3958
4659
  };
3959
4660
  }
3960
4661
  // src/tools/list-package-docs.ts
3961
- var argsSchema3 = {
4662
+ var argsSchema4 = {
3962
4663
  registry: schemas.registry,
3963
4664
  package_name: schemas.packageName.describe("Name of the package to list documentation for"),
3964
4665
  version: schemas.version
@@ -3966,8 +4667,8 @@ var argsSchema3 = {
3966
4667
  function createListPackageDocsTool(pkgseerService) {
3967
4668
  return {
3968
4669
  name: "list_package_docs",
3969
- description: "Lists documentation pages for a package version. Returns page titles, globally unique IDs, and metadata. Use this to discover available documentation before fetching specific pages.",
3970
- schema: argsSchema3,
4670
+ description: "Discover available documentation pages for a package. Start here before fetching or searching docs. " + "Returns: page titles, unique IDs (needed for fetch_package_doc), word counts, and update timestamps. " + "Workflow: list_package_docs fetch_package_doc for full content, or search_package_docs to find specific topics.",
4671
+ schema: argsSchema4,
3971
4672
  handler: async ({ registry, package_name, version: version2 }, _extra) => {
3972
4673
  return withErrorHandling("list package documentation", async () => {
3973
4674
  const result = await pkgseerService.listPackageDocs(toGraphQLRegistry2(registry), package_name, version2);
@@ -3983,13 +4684,13 @@ function createListPackageDocsTool(pkgseerService) {
3983
4684
  };
3984
4685
  }
3985
4686
  // src/tools/package-dependencies.ts
3986
- import { z as z5 } from "zod";
3987
- var argsSchema4 = {
4687
+ import { z as z6 } from "zod";
4688
+ var argsSchema5 = {
3988
4689
  registry: schemas.registry,
3989
4690
  package_name: schemas.packageName.describe("Name of the package to retrieve dependencies for"),
3990
4691
  version: schemas.version,
3991
- include_transitive: z5.boolean().optional().describe("Whether to include transitive dependency DAG"),
3992
- max_depth: z5.number().int().min(1).max(10).optional().describe("Maximum depth for transitive traversal (1-10)")
4692
+ include_transitive: z6.boolean().optional().describe("Whether to include transitive dependency DAG"),
4693
+ max_depth: z6.number().int().min(1).max(10).optional().describe("Maximum depth for transitive traversal (1-10)")
3993
4694
  };
3994
4695
  function decodeDag(rawDag) {
3995
4696
  if (!rawDag || typeof rawDag !== "object")
@@ -4066,8 +4767,8 @@ function buildEdgeDepths(decodedDag, rootId) {
4066
4767
  function createPackageDependenciesTool(pkgseerService) {
4067
4768
  return {
4068
4769
  name: "package_dependencies",
4069
- description: "Retrieves direct and transitive dependencies for a package version. Options: include_transitive (bool) and max_depth (1-10). Transitive output includes a DAG (`n/e/v` keys) decoded into readable nodes/edges with depth and counts; raw DAG is preserved alongside decoded form.",
4070
- schema: argsSchema4,
4770
+ description: "Analyze package dependencies. By default returns direct dependencies only. " + "Set include_transitive=true to get the full dependency tree (use max_depth to limit large trees). " + "Returns: dependency names, version constraints, and for transitive deps, a graph with depth levels. " + "Use this to understand complexity before adding a package, or to find nested dependencies.",
4771
+ schema: argsSchema5,
4071
4772
  handler: async ({ registry, package_name, version: version2, include_transitive, max_depth }, _extra) => {
4072
4773
  return withErrorHandling("fetch package dependencies", async () => {
4073
4774
  const result = await pkgseerService.getPackageDependencies(toGraphQLRegistry2(registry), package_name, version2, include_transitive, max_depth);
@@ -4105,7 +4806,7 @@ function createPackageDependenciesTool(pkgseerService) {
4105
4806
  };
4106
4807
  }
4107
4808
  // src/tools/package-quality.ts
4108
- var argsSchema5 = {
4809
+ var argsSchema6 = {
4109
4810
  registry: schemas.registry,
4110
4811
  package_name: schemas.packageName.describe("Name of the package to analyze"),
4111
4812
  version: schemas.version
@@ -4113,8 +4814,8 @@ var argsSchema5 = {
4113
4814
  function createPackageQualityTool(pkgseerService) {
4114
4815
  return {
4115
4816
  name: "package_quality",
4116
- description: "Retrieves quality score and rule-level breakdown for a package",
4117
- schema: argsSchema5,
4817
+ description: "Evaluate package maintenance health and code quality. Use this to assess if a package is well-maintained " + "before adding it as a dependency. Returns: overall score (0-100), category scores (documentation, " + "testing, community, maintenance), and individual rule results with pass/fail status. " + "Useful for comparing quality between alternative packages.",
4818
+ schema: argsSchema6,
4118
4819
  handler: async ({ registry, package_name, version: version2 }, _extra) => {
4119
4820
  return withErrorHandling("fetch package quality", async () => {
4120
4821
  const result = await pkgseerService.getPackageQuality(toGraphQLRegistry2(registry), package_name, version2);
@@ -4130,15 +4831,15 @@ function createPackageQualityTool(pkgseerService) {
4130
4831
  };
4131
4832
  }
4132
4833
  // src/tools/package-summary.ts
4133
- var argsSchema6 = {
4834
+ var argsSchema7 = {
4134
4835
  registry: schemas.registry,
4135
4836
  package_name: schemas.packageName.describe("Name of the package to retrieve summary for")
4136
4837
  };
4137
4838
  function createPackageSummaryTool(pkgseerService) {
4138
4839
  return {
4139
4840
  name: "package_summary",
4140
- description: "Retrieves comprehensive package summary including metadata, versions, security advisories, and quickstart information",
4141
- schema: argsSchema6,
4841
+ description: "Get a comprehensive overview of a package. Use this as your first tool when researching a package. " + "Returns: description, latest version, license, repository URL, download stats, " + "active security advisories count, and quickstart/installation instructions. " + "For deeper analysis, follow up with package_quality (maintenance health) or " + "package_vulnerabilities (security details).",
4842
+ schema: argsSchema7,
4142
4843
  handler: async ({ registry, package_name }, _extra) => {
4143
4844
  return withErrorHandling("fetch package summary", async () => {
4144
4845
  const result = await pkgseerService.getPackageSummary(toGraphQLRegistry2(registry), package_name);
@@ -4154,7 +4855,7 @@ function createPackageSummaryTool(pkgseerService) {
4154
4855
  };
4155
4856
  }
4156
4857
  // src/tools/package-vulnerabilities.ts
4157
- var argsSchema7 = {
4858
+ var argsSchema8 = {
4158
4859
  registry: schemas.registry,
4159
4860
  package_name: schemas.packageName.describe("Name of the package to inspect for vulnerabilities"),
4160
4861
  version: schemas.version
@@ -4162,8 +4863,8 @@ var argsSchema7 = {
4162
4863
  function createPackageVulnerabilitiesTool(pkgseerService) {
4163
4864
  return {
4164
4865
  name: "package_vulnerabilities",
4165
- description: "Retrieves vulnerability details for a package, including affected version ranges and upgrade guidance",
4166
- schema: argsSchema7,
4866
+ description: "Check security vulnerabilities for a package. Use this before adding dependencies or when auditing existing ones. " + "Returns: list of CVEs/advisories with severity levels, affected version ranges, fixed versions, " + "and upgrade recommendations. If version is specified, shows only vulnerabilities affecting that version.",
4867
+ schema: argsSchema8,
4167
4868
  handler: async ({ registry, package_name, version: version2 }, _extra) => {
4168
4869
  return withErrorHandling("fetch package vulnerabilities", async () => {
4169
4870
  const result = await pkgseerService.getPackageVulnerabilities(toGraphQLRegistry2(registry), package_name, version2);
@@ -4178,79 +4879,126 @@ function createPackageVulnerabilitiesTool(pkgseerService) {
4178
4879
  }
4179
4880
  };
4180
4881
  }
4181
- // src/tools/search-package-docs.ts
4182
- import { z as z6 } from "zod";
4183
- var argsSchema8 = {
4882
+ // src/tools/search.ts
4883
+ import { z as z7 } from "zod";
4884
+ var packageInputSchema2 = z7.object({
4184
4885
  registry: schemas.registry,
4185
- package_name: schemas.packageName.describe("Name of the package to search documentation for"),
4186
- terms: z6.array(z6.string()).optional().describe("Search terms to match. Provide a few key words or phrases that should appear in results."),
4187
- match_mode: z6.enum(["any", "or", "all", "and"]).optional().describe('How to combine terms: "any" (OR) or "all" (AND).'),
4188
- include_snippets: z6.boolean().optional().describe("Include content excerpts around matches"),
4189
- limit: z6.number().int().min(1).max(100).optional().describe("Maximum number of results to return"),
4190
- version: schemas.version
4886
+ name: z7.string().min(1).describe("Package name"),
4887
+ version: z7.string().optional().describe("Specific version (defaults to latest)")
4888
+ });
4889
+ var argsSchema9 = {
4890
+ packages: z7.array(packageInputSchema2).min(1).max(20).describe("Packages to search (1-20). Each package needs registry and name."),
4891
+ query: z7.string().min(1).describe("Search query - natural language or keywords"),
4892
+ mode: z7.enum(["all", "code", "docs"]).optional().describe('Search mode: "all" (default), "code" only, or "docs" only'),
4893
+ limit: z7.number().int().min(1).max(100).optional().describe("Maximum results (default: 20)")
4191
4894
  };
4192
- function createSearchPackageDocsTool(pkgseerService) {
4895
+ function toSearchMode2(mode) {
4896
+ if (!mode)
4897
+ return;
4898
+ const map = {
4899
+ all: "ALL",
4900
+ code: "CODE",
4901
+ docs: "DOCS"
4902
+ };
4903
+ return map[mode.toLowerCase()];
4904
+ }
4905
+ function formatIndexingWarnings2(indexingStatus) {
4906
+ if (!indexingStatus || indexingStatus.length === 0)
4907
+ return null;
4908
+ const warnings = [];
4909
+ for (const status of indexingStatus) {
4910
+ if (!status)
4911
+ continue;
4912
+ const pkg = `${status.packageName}@${status.version ?? "latest"} (${status.registry?.toLowerCase() ?? "unknown"})`;
4913
+ if (status.codeStatus && status.codeStatus !== "INDEXED") {
4914
+ warnings.push(` - ${pkg}: code ${status.codeStatus.toLowerCase()}`);
4915
+ }
4916
+ if (status.docsStatus && status.docsStatus !== "INDEXED") {
4917
+ warnings.push(` - ${pkg}: docs ${status.docsStatus.toLowerCase()}`);
4918
+ }
4919
+ }
4920
+ if (warnings.length === 0)
4921
+ return null;
4922
+ return `
4923
+ Note: Some packages are not fully indexed:
4924
+ ` + warnings.join(`
4925
+ `) + `
4926
+ Results may be incomplete. Retry later for more complete results.`;
4927
+ }
4928
+ function createSearchTool(pkgseerService) {
4193
4929
  return {
4194
- name: "search_package_docs",
4195
- description: "Searches package documentation using search terms and match modes (any/all). Returns ranked results with match context. Defaults to include_snippets=true.",
4196
- schema: argsSchema8,
4197
- handler: async ({
4198
- registry,
4199
- package_name,
4200
- terms,
4201
- match_mode,
4202
- include_snippets,
4203
- limit,
4204
- version: version2
4205
- }, _extra) => {
4206
- return withErrorHandling("search package documentation", async () => {
4207
- const normalizedTerms = terms?.map((term) => term.trim()).filter((term) => term.length > 0) ?? [];
4208
- if (normalizedTerms.length === 0) {
4209
- return errorResult("Search terms are required to run documentation search.");
4930
+ name: "search",
4931
+ 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.",
4932
+ schema: argsSchema9,
4933
+ handler: async ({ packages, query, mode, limit }, _extra) => {
4934
+ return withErrorHandling("search packages", async () => {
4935
+ const normalizedQuery = query.trim();
4936
+ if (normalizedQuery.length === 0) {
4937
+ return errorResult("Search query is required.");
4210
4938
  }
4211
- const matchMode = match_mode === "all" || match_mode === "and" ? "AND" : match_mode === "any" || match_mode === "or" ? "OR" : undefined;
4212
- const includeSnippets = include_snippets ?? true;
4213
- const result = await pkgseerService.searchPackageDocs(toGraphQLRegistry2(registry), package_name, {
4214
- keywords: normalizedTerms,
4215
- matchMode,
4216
- includeSnippets,
4217
- limit,
4218
- version: version2
4939
+ const graphqlPackages = packages.map((pkg) => ({
4940
+ registry: toGraphQLRegistry2(pkg.registry),
4941
+ name: pkg.name,
4942
+ version: pkg.version
4943
+ }));
4944
+ const result = await pkgseerService.combinedSearch(graphqlPackages, normalizedQuery, {
4945
+ mode: toSearchMode2(mode),
4946
+ limit
4219
4947
  });
4220
4948
  const graphqlError = handleGraphQLErrors(result.errors);
4221
4949
  if (graphqlError)
4222
4950
  return graphqlError;
4223
- if (!result.data.searchPackageDocs) {
4224
- return errorResult(`No documentation found for ${package_name} in ${registry}`);
4951
+ if (!result.data.combinedSearch) {
4952
+ return errorResult("No search results returned. Check package names and registries.");
4225
4953
  }
4226
- if ((result.data.searchPackageDocs.entries ?? []).length === 0 && normalizedTerms.length > 0) {
4227
- return errorResult(`No documentation matched: ${normalizedTerms.join(", ")}. ` + "Try fewer or broader terms, or reduce match constraints.");
4954
+ const searchResult = result.data.combinedSearch;
4955
+ const entries = searchResult.entries ?? [];
4956
+ if (entries.length === 0) {
4957
+ return errorResult(`No results found for "${normalizedQuery}". ` + "Try broader terms or different packages.");
4958
+ }
4959
+ let response = JSON.stringify(searchResult, null, 2);
4960
+ const indexingWarning = formatIndexingWarnings2(searchResult.indexingStatus);
4961
+ if (indexingWarning) {
4962
+ response += indexingWarning;
4228
4963
  }
4229
- return textResult(JSON.stringify(result.data.searchPackageDocs, null, 2));
4964
+ return textResult(response);
4230
4965
  });
4231
4966
  }
4232
4967
  };
4233
4968
  }
4234
4969
  // src/tools/search-project-docs.ts
4235
- import { z as z7 } from "zod";
4236
- var argsSchema9 = {
4237
- project: z7.string().optional().describe("Project name to search. Optional if configured in pkgseer.yml; only needed to search a different project."),
4238
- terms: z7.array(z7.string()).optional().describe("Search terms to match. Provide a few key words or phrases that should appear in results."),
4239
- match_mode: z7.enum(["any", "or", "all", "and"]).optional().describe('How to combine terms: "any" (OR) or "all" (AND).'),
4240
- include_snippets: z7.boolean().optional().describe("Include content excerpts around matches"),
4241
- limit: z7.number().int().min(1).max(100).optional().describe("Maximum number of results to return")
4970
+ import { z as z8 } from "zod";
4971
+ var argsSchema10 = {
4972
+ project_directory: z8.string().optional().describe("Directory to search for pkgseer.yml configuration. " + "Use this to specify which project's context to use when the MCP server " + "is installed at user level. Defaults to MCP server's working directory."),
4973
+ project: z8.string().optional().describe("Project name to search. Overrides value from pkgseer.yml if provided."),
4974
+ terms: z8.array(z8.string()).optional().describe("Search terms to match. Provide a few key words or phrases that should appear in results."),
4975
+ match_mode: z8.enum(["any", "or", "all", "and"]).optional().describe('How to combine terms: "any" (OR) or "all" (AND).'),
4976
+ include_snippets: z8.boolean().optional().describe("Include content excerpts around matches"),
4977
+ limit: z8.number().int().min(1).max(100).optional().describe("Maximum number of results to return")
4242
4978
  };
4243
4979
  function createSearchProjectDocsTool(deps) {
4244
- const { pkgseerService, config } = deps;
4980
+ const { pkgseerService, configService, defaultProjectDir } = deps;
4245
4981
  return {
4246
4982
  name: "search_project_docs",
4247
- description: "Searches documentation across all dependencies in a PkgSeer project using search terms and match modes (any/all). Returns ranked results from multiple packages. Uses project from pkgseer.yml config by default.",
4248
- schema: argsSchema9,
4249
- handler: async ({ project, terms, match_mode, include_snippets, limit }, _extra) => {
4983
+ description: "Searches documentation across all dependencies in a PkgSeer project using search terms and match modes (any/all). Returns ranked results from multiple packages. Use project_directory to specify which project's context to use, or project to search a specific project directly.",
4984
+ schema: argsSchema10,
4985
+ handler: async ({
4986
+ project_directory,
4987
+ project,
4988
+ terms,
4989
+ match_mode,
4990
+ include_snippets,
4991
+ limit
4992
+ }, _extra) => {
4250
4993
  return withErrorHandling("search project documentation", async () => {
4251
- const resolvedProject = project ?? config.project;
4994
+ const targetDir = project_directory ?? defaultProjectDir;
4995
+ const projectConfig = await configService.loadProjectConfigFrom(targetDir);
4996
+ const resolvedProject = project ?? projectConfig?.config.project;
4252
4997
  if (!resolvedProject) {
4253
- return errorResult("No project provided and none configured in pkgseer.yml. " + "Either pass project parameter or add project to your config.");
4998
+ return errorResult(`No project specified and no pkgseer.yml found. To fix:
4999
+ ` + ` 1. Pass project_directory: '/path/to/project' (folder containing pkgseer.yml)
5000
+ ` + ` 2. Pass project: 'project-name' directly if you know the project name
5001
+ ` + " 3. Run 'pkgseer project init' to create pkgseer.yml in your project");
4254
5002
  }
4255
5003
  const normalizedTerms = terms?.map((term) => term.trim()).filter((term) => term.length > 0) ?? [];
4256
5004
  if (normalizedTerms.length === 0) {
@@ -4287,8 +5035,13 @@ var TOOL_FACTORIES = {
4287
5035
  compare_packages: ({ pkgseerService }) => createComparePackagesTool(pkgseerService),
4288
5036
  list_package_docs: ({ pkgseerService }) => createListPackageDocsTool(pkgseerService),
4289
5037
  fetch_package_doc: ({ pkgseerService }) => createFetchPackageDocTool(pkgseerService),
4290
- search_package_docs: ({ pkgseerService }) => createSearchPackageDocsTool(pkgseerService),
4291
- search_project_docs: ({ pkgseerService, config }) => createSearchProjectDocsTool({ pkgseerService, config })
5038
+ search: ({ pkgseerService }) => createSearchTool(pkgseerService),
5039
+ fetch_code_context: ({ pkgseerService }) => createFetchCodeContextTool(pkgseerService),
5040
+ search_project_docs: ({ pkgseerService, configService, defaultProjectDir }) => createSearchProjectDocsTool({
5041
+ pkgseerService,
5042
+ configService,
5043
+ defaultProjectDir
5044
+ })
4292
5045
  };
4293
5046
  var PUBLIC_READ_TOOLS = [
4294
5047
  "package_summary",
@@ -4298,21 +5051,29 @@ var PUBLIC_READ_TOOLS = [
4298
5051
  "compare_packages",
4299
5052
  "list_package_docs",
4300
5053
  "fetch_package_doc",
4301
- "search_package_docs"
5054
+ "search",
5055
+ "fetch_code_context"
4302
5056
  ];
4303
5057
  var PROJECT_READ_TOOLS = ["search_project_docs"];
4304
5058
  var ALL_TOOLS = [...PUBLIC_READ_TOOLS, ...PROJECT_READ_TOOLS];
4305
5059
  function createMcpServer(deps) {
4306
- const { pkgseerService, config } = deps;
5060
+ const { pkgseerService, configService, config, fileSystemService } = deps;
4307
5061
  const server = new McpServer({
4308
5062
  name: "pkgseer",
4309
5063
  version: "0.1.0"
4310
5064
  });
5065
+ const defaultProjectDir = fileSystemService.getCwd();
4311
5066
  const enabledToolNames = config.enabled_tools ?? ALL_TOOLS;
4312
5067
  const toolsToRegister = enabledToolNames.filter((name) => ALL_TOOLS.includes(name));
5068
+ const factoryDeps = {
5069
+ pkgseerService,
5070
+ configService,
5071
+ config,
5072
+ defaultProjectDir
5073
+ };
4313
5074
  for (const toolName of toolsToRegister) {
4314
5075
  const factory = TOOL_FACTORIES[toolName];
4315
- const tool = factory({ pkgseerService, config });
5076
+ const tool = factory(factoryDeps);
4316
5077
  server.registerTool(tool.name, { description: tool.description, inputSchema: tool.schema }, tool.handler);
4317
5078
  }
4318
5079
  return server;
@@ -5259,103 +6020,13 @@ function registerProjectUploadCommand(program) {
5259
6020
  });
5260
6021
  });
5261
6022
  }
5262
- // src/commands/quickstart.ts
5263
- var QUICKSTART_TEXT = `# PkgSeer CLI - Quick Reference for AI Agents
5264
-
5265
- PkgSeer provides package intelligence for npm, PyPI, and Hex registries.
5266
-
5267
- ## Package Commands (pkgseer pkg)
5268
-
5269
- ### Get package info
5270
- \`pkgseer pkg info <package> [-r npm|pypi|hex] [--json]\`
5271
- Returns: metadata, versions, security advisories, quickstart
5272
-
5273
- ### Check quality score
5274
- \`pkgseer pkg quality <package> [-r registry] [-v pkg-version] [--json]\`
5275
- Returns: overall score, category breakdown, rule details
5276
-
5277
- ### List dependencies
5278
- \`pkgseer pkg deps <package> [-r registry] [-v pkg-version] [-t] [-d depth] [--json]\`
5279
- Returns: direct deps, transitive count, dependency tree (with -t)
5280
-
5281
- ### Check vulnerabilities
5282
- \`pkgseer pkg vulns <package> [-r registry] [-v pkg-version] [--json]\`
5283
- Returns: CVEs, severity, affected versions, upgrade guidance
5284
-
5285
- ### Compare packages
5286
- \`pkgseer pkg compare <pkg1> <pkg2> [...] [--json]\`
5287
- Format: [registry:]name[@version] (e.g., npm:lodash, pypi:requests@2.31.0)
5288
- Returns: side-by-side quality, downloads, vulnerabilities
5289
-
5290
- ## Documentation Commands (pkgseer docs)
5291
-
5292
- ### List doc pages
5293
- \`pkgseer docs list <package> [-r registry] [-v pkg-version] [--json]\`
5294
- Returns: page titles, IDs (slugs), word counts
5295
-
5296
- ### Get doc page
5297
- \`pkgseer docs get <package>/<page-id> [<package>/<page-id> ...] [-r registry] [-v pkg-version] [--json]\`
5298
- Returns: full page content (markdown), breadcrumbs, source URL
5299
- Supports multiple pages: \`pkgseer docs get express/readme express/api\`
5300
-
5301
- ### Search docs
5302
- \`pkgseer docs search "<query>" [-p package] [-r registry] [--json]\`
5303
- \`pkgseer docs search --keywords term1,term2 [-s] [-l limit]\`
5304
- Default: searches project docs (if project configured)
5305
- With -p: searches specific package docs
5306
- Returns: ranked results with relevance scores
5307
-
5308
- ## Tips for AI Agents
5309
-
5310
- 1. Use \`--json\` for structured data parsing
5311
- 2. Default registry is npm; use \`-r pypi\` or \`-r hex\` for others
5312
- 3. Version defaults to latest; use \`-v <version>\` for specific version
5313
- 4. For docs search, configure project in pkgseer.yml for project-wide search
5314
- 5. Compare up to 10 packages at once with \`pkg compare\`
5315
-
5316
- ## MCP Server
5317
-
5318
- For Model Context Protocol integration:
5319
- \`pkgseer mcp\`
5320
-
5321
- Add to MCP config:
5322
- {
5323
- "pkgseer": {
5324
- "command": "pkgseer",
5325
- "args": ["mcp"]
5326
- }
5327
- }
5328
- `;
5329
- function quickstartAction(options) {
5330
- if (options.json) {
5331
- console.log(JSON.stringify({
5332
- version,
5333
- quickstart: QUICKSTART_TEXT
5334
- }));
5335
- } else {
5336
- console.log(QUICKSTART_TEXT);
5337
- }
5338
- }
5339
- var QUICKSTART_DESCRIPTION = `Show quick reference for AI agents.
5340
-
5341
- Displays a concise guide to pkgseer CLI commands optimized
5342
- for LLM agents. Includes command syntax, options, and tips
5343
- for effective usage.
5344
-
5345
- Use --json for structured output.`;
5346
- function registerQuickstartCommand(program) {
5347
- program.command("quickstart").summary("Show quick reference for AI agents").description(QUICKSTART_DESCRIPTION).option("--json", "Output as JSON").action((options) => {
5348
- quickstartAction(options);
5349
- });
5350
- }
5351
-
5352
6023
  // src/cli.ts
5353
6024
  var program = new Command;
5354
6025
  program.name("pkgseer").description("Package intelligence for your AI assistant").version(version).addHelpText("after", `
5355
6026
  Getting started:
5356
- pkgseer init Interactive setup wizard (project + MCP)
6027
+ pkgseer init Interactive setup wizard (project + AI)
5357
6028
  pkgseer login Authenticate with your account
5358
- pkgseer quickstart Quick reference for AI agents
6029
+ pkgseer skill init Install AI agent skill
5359
6030
 
5360
6031
  Package commands:
5361
6032
  pkgseer pkg info <package> Get package summary
@@ -5372,9 +6043,9 @@ Documentation commands:
5372
6043
  Learn more at https://pkgseer.dev`);
5373
6044
  registerInitCommand(program);
5374
6045
  registerMcpCommand(program);
6046
+ registerSkillCommand(program);
5375
6047
  registerLoginCommand(program);
5376
6048
  registerLogoutCommand(program);
5377
- registerQuickstartCommand(program);
5378
6049
  var auth = program.command("auth").description("View and manage authentication");
5379
6050
  registerAuthStatusCommand(auth);
5380
6051
  var config = program.command("config").description("View and manage configuration");