From e7c110f07dd14d9c61b3251e0ead6adbbea17d25 Mon Sep 17 00:00:00 2001 From: edy Date: Fri, 6 Mar 2026 21:17:13 +0800 Subject: [PATCH 1/3] =?UTF-8?q?fix(cli):=20=E7=A9=BA=E5=85=B3=E9=94=AE?= =?UTF-8?q?=E8=AF=8D=E5=9B=9E=E9=80=80=E6=9C=AC=E5=9C=B0=E6=90=9C=E7=B4=A2?= =?UTF-8?q?=EF=BC=9BSDK=20=E5=A4=8D=E7=94=A8=20FederatedSearchParams=20?= =?UTF-8?q?=E4=B8=8E=20limit=20=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CLI search: 非 --local 且无 keyword 时回退到 searchGenes,避免空串请求联邦搜索 - SDK: federatedSearch 参数改用 FederatedSearchParams 类型 - SDK: limit 仅当正有限数时写入 URL Made-with: Cursor --- packages/cli/src/commands/search.ts | 40 ++++++++++++++++++++++++++- packages/sdk/typescript/src/client.ts | 11 ++++---- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/packages/cli/src/commands/search.ts b/packages/cli/src/commands/search.ts index 766bf2b..1f6dca8 100644 --- a/packages/cli/src/commands/search.ts +++ b/packages/cli/src/commands/search.ts @@ -19,6 +19,8 @@ export const searchCommand = new Command('search') const client = new GeneHubClient({ registryUrl: config.registryUrl, token: config.token }); try { + const trimmedKeyword = keyword?.trim(); + if (opts.local) { const result = await client.searchGenes({ q: keyword, @@ -55,8 +57,44 @@ export const searchCommand = new Command('search') return; } + if (!trimmedKeyword) { + const result = await client.searchGenes({ + q: undefined, + category: opts.category, + tags: opts.tags?.split(','), + compatibility: opts.compat, + sort: opts.sort, + page: Number(opts.page), + }); + + if (opts.json) { + console.log(JSON.stringify(result, null, 2)); + return; + } + + if (result.items.length === 0) { + output.info('未找到匹配的基因'); + return; + } + + output.table( + ['slug', '名称', '版本', '分类', '兼容', '安装数'], + result.items.map((g) => [ + g.slug, + g.name, + g.version, + g.category, + (g.compatibility as string[]).join(', '), + String(g.install_count), + ]), + ); + + output.info(`共 ${result.total} 条结果,第 ${result.page}/${result.total_pages} 页`); + return; + } + const result = await client.federatedSearch({ - q: keyword ?? '', + q: trimmedKeyword, category: opts.category, limit: Number(opts.limit) || 20, }); diff --git a/packages/sdk/typescript/src/client.ts b/packages/sdk/typescript/src/client.ts index 9b0a57e..2f82246 100644 --- a/packages/sdk/typescript/src/client.ts +++ b/packages/sdk/typescript/src/client.ts @@ -5,6 +5,7 @@ import type { ApiResponse, CreateAgentTemplateRequest, CreateGenomeRequest, + FederatedSearchParams, FederatedSearchResult, Gene, GeneListParams, @@ -73,15 +74,13 @@ export class GeneHubClient { } /** 联邦搜索:合并本地 DB 与外部源(如 ClawHub)结果 */ - async federatedSearch(params: { - q: string; - category?: string; - limit?: number; - }): Promise { + async federatedSearch(params: FederatedSearchParams): Promise { const qs = new URLSearchParams(); qs.set('q', params.q); if (params.category) qs.set('category', params.category); - if (params.limit != null) qs.set('limit', String(params.limit)); + if (Number.isFinite(params.limit) && (params.limit as number) > 0) { + qs.set('limit', String(params.limit)); + } return this.request(`/api/v1/genes/search?${qs.toString()}`); } From a8454236ec3f73e965902b2a8f064958df786dd0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 6 Mar 2026 13:24:42 +0000 Subject: [PATCH 2/3] Initial plan From bc2ff578c96e96aaf14a1d27984f50e25298a937 Mon Sep 17 00:00:00 2001 From: edy Date: Sat, 7 Mar 2026 10:32:13 +0800 Subject: [PATCH 3/3] =?UTF-8?q?feat(cli):=20=E6=96=B0=E5=A2=9E=20info=20?= =?UTF-8?q?=E5=91=BD=E4=BB=A4=E6=9F=A5=E7=9C=8B=E5=9F=BA=E5=9B=A0=E8=AF=A6?= =?UTF-8?q?=E6=83=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - info.ts: loadConfig + getGene,formatGene 输出,支持 --json - index.ts: 注册 infoCommand - commands.test.ts: 预期列表加入 info Made-with: Cursor --- packages/cli/src/__tests__/commands.test.ts | 1 + packages/cli/src/commands/info.ts | 65 +++++++++++++++++++++ packages/cli/src/index.ts | 2 + 3 files changed, 68 insertions(+) create mode 100644 packages/cli/src/commands/info.ts diff --git a/packages/cli/src/__tests__/commands.test.ts b/packages/cli/src/__tests__/commands.test.ts index 8b9ba5e..42618cd 100644 --- a/packages/cli/src/__tests__/commands.test.ts +++ b/packages/cli/src/__tests__/commands.test.ts @@ -173,6 +173,7 @@ describe('command registration', () => { 'install', 'uninstall', 'search', + 'info', 'list', 'publish', 'init', diff --git a/packages/cli/src/commands/info.ts b/packages/cli/src/commands/info.ts new file mode 100644 index 0000000..f72245d --- /dev/null +++ b/packages/cli/src/commands/info.ts @@ -0,0 +1,65 @@ +import { GeneHubClient } from '@nodeskai/genehub-sdk'; +import type { Gene } from '@nodeskai/genehub-types'; +import { Command } from 'commander'; +import { loadConfig } from '../config.js'; +import * as output from '../output.js'; + +function formatAuthor(gene: Gene): string { + const a = gene.author; + if (!a) return '-'; + if (a.ref) return `${a.name} (${a.ref})`; + return `${a.name} (${a.type})`; +} + +function formatGene(gene: Gene): void { + console.log(` ${gene.slug} v${gene.version}`); + console.log(` ${gene.short_description || gene.description || ''}`); + console.log(''); + console.log(` Category: ${gene.category}`); + console.log(` Tags: ${gene.tags?.join(', ') || '(none)'}`); + console.log(` Author: ${formatAuthor(gene)}`); + console.log(` Status: ${gene.is_published ? 'published' : 'unpublished'}`); + console.log(` Downloads: ${gene.install_count}`); + console.log(''); + console.log(' Compatibility:'); + if (gene.compatibility?.length) { + for (const c of gene.compatibility) { + console.log(` - ${c}`); + } + } else { + console.log(' (none)'); + } + console.log(''); + console.log( + ' Dependencies:', + gene.dependencies?.length + ? gene.dependencies.map((d) => `${d.slug}@${d.version}`).join(', ') + : '(none)', + ); + console.log(''); + console.log(' Install:'); + console.log(` genehub install ${gene.slug}`); +} + +export const infoCommand = new Command('info') + .description('查看基因详情') + .argument('', '基因 slug') + .option('--json', 'JSON 格式输出', false) + .action(async (slug: string, opts: { json?: boolean }) => { + const config = await loadConfig(); + const client = new GeneHubClient({ registryUrl: config.registryUrl, token: config.token }); + + try { + const gene = await client.getGene(slug); + + if (opts.json) { + console.log(JSON.stringify(gene, null, 2)); + return; + } + + formatGene(gene); + } catch (err) { + output.fail(err instanceof Error ? err.message : String(err)); + process.exit(1); + } + }); diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 3ae70b0..4913400 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -5,6 +5,7 @@ import { Command } from 'commander'; import { authCommand } from './commands/auth.js'; import { configCommand } from './commands/config.js'; import { genomeCommand } from './commands/genome.js'; +import { infoCommand } from './commands/info.js'; import { initCommand } from './commands/init.js'; import { installCommand } from './commands/install.js'; import { learnCommand } from './commands/learn.js'; @@ -34,6 +35,7 @@ program.addCommand(authCommand); program.addCommand(installCommand); program.addCommand(uninstallCommand); program.addCommand(searchCommand); +program.addCommand(infoCommand); program.addCommand(listCommand); program.addCommand(publishCommand); program.addCommand(initCommand);