(query: string)
| 478 | } |
| 479 | |
| 480 | async function searchCommand(query: string): Promise<void> { |
| 481 | trackEvent("command", { name: "search" }); |
| 482 | log.blank(); |
| 483 | const spinner = ora(`Searching for "${query}"...`).start(); |
| 484 | |
| 485 | let data; |
| 486 | try { |
| 487 | data = await searchSkills(query); |
| 488 | } catch (err) { |
| 489 | spinner.fail(pc.red(`Error: ${err instanceof Error ? err.message : String(err)}`)); |
| 490 | return; |
| 491 | } |
| 492 | |
| 493 | if (data.error) { |
| 494 | spinner.fail(pc.red(`Error: ${data.message || data.error}`)); |
| 495 | return; |
| 496 | } |
| 497 | |
| 498 | if (!data.results || data.results.length === 0) { |
| 499 | spinner.warn(pc.yellow(`No skills found matching "${query}"`)); |
| 500 | return; |
| 501 | } |
| 502 | |
| 503 | spinner.succeed(`Found ${data.results.length} skill(s)`); |
| 504 | trackEvent("search_query", { query, resultCount: data.results.length }); |
| 505 | log.blank(); |
| 506 | |
| 507 | const indexWidth = data.results.length.toString().length; |
| 508 | const nameWithRepo = (s: SkillSearchResult) => `${s.name} ${pc.dim(`(${s.project})`)}`; |
| 509 | const nameWithRepoLen = (s: SkillSearchResult) => `${s.name} (${s.project})`.length; |
| 510 | const maxNameLen = Math.max(...data.results.map(nameWithRepoLen)); |
| 511 | const popularityColWidth = 13; |
| 512 | const choices = data.results.map((s, index) => { |
| 513 | const indexStr = pc.dim(`${(index + 1).toString().padStart(indexWidth)}.`); |
| 514 | const rawLen = nameWithRepoLen(s); |
| 515 | const displayName = nameWithRepo(s) + " ".repeat(maxNameLen - rawLen); |
| 516 | const popularity = formatPopularity(s.installCount) + " ".repeat(popularityColWidth - 4); |
| 517 | const trust = formatTrust(s.trustScore); |
| 518 | |
| 519 | const skillLink = terminalLink(s.name, s.url || `https://github.com${s.project}`, pc.white); |
| 520 | const repoLink = terminalLink(s.project, `https://github.com${s.project}`, pc.white); |
| 521 | const metadataLines = [ |
| 522 | pc.dim("─".repeat(50)), |
| 523 | "", |
| 524 | `${pc.yellow("Skill:")} ${skillLink}`, |
| 525 | `${pc.yellow("Repo:")} ${repoLink}`, |
| 526 | `${pc.yellow("Installs:")} ${pc.white(formatInstallRange(s.installCount))}`, |
| 527 | `${pc.yellow("Trust:")} ${s.trustScore !== undefined && s.trustScore >= 0 ? pc.white(s.trustScore.toFixed(1)) : pc.dim("-")}`, |
| 528 | `${pc.yellow("Description:")}`, |
| 529 | pc.white(s.description || "No description"), |
| 530 | ]; |
| 531 | |
| 532 | return { |
| 533 | name: `${indexStr} ${displayName} ${popularity}${trust}`, |
| 534 | value: s, |
| 535 | description: metadataLines.join("\n"), |
| 536 | }; |
| 537 | }); |
no test coverage detected