@zhin.js/cli 1.0.7 → 1.0.8

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.
@@ -7,16 +7,17 @@ import { execSync } from 'node:child_process';
7
7
 
8
8
  interface NewPluginOptions {
9
9
  skipInstall?: boolean;
10
+ isOfficial?: boolean;
10
11
  }
11
12
 
12
13
  export const newCommand = new Command('new')
13
14
  .description('创建插件包模板')
14
15
  .argument('[plugin-name]', '插件名称(如: my-plugin)')
16
+ .option('--is-official', '是否为官方插件', false)
15
17
  .option('--skip-install', '跳过依赖安装', false)
16
18
  .action(async (pluginName: string, options: NewPluginOptions) => {
17
19
  try {
18
20
  let name = pluginName;
19
-
20
21
  if (!name) {
21
22
  const { pluginName: inputName } = await inquirer.prompt([
22
23
  {
@@ -53,7 +54,7 @@ export const newCommand = new Command('new')
53
54
  await createPluginPackage(pluginDir, name, options);
54
55
 
55
56
  // 自动添加到 app/package.json
56
- await addPluginToApp(name);
57
+ await addPluginToApp(name, options.isOfficial);
57
58
 
58
59
  logger.success(`✓ 插件包 ${name} 创建成功!`);
59
60
  logger.log('');
@@ -76,11 +77,11 @@ export const newCommand = new Command('new')
76
77
 
77
78
  async function createPluginPackage(pluginDir: string, pluginName: string, options: NewPluginOptions) {
78
79
  const capitalizedName = pluginName.charAt(0).toUpperCase() + pluginName.slice(1).replace(/-([a-z])/g, (_, c) => c.toUpperCase());
79
- const packageName = `@zhin.js/${pluginName}`;
80
+ const packageName = options.isOfficial ? `@zhin.js/${pluginName}` : `zhin.js-${pluginName}`;
80
81
 
81
82
  // 创建目录结构
82
83
  await fs.ensureDir(pluginDir);
83
- await fs.ensureDir(path.join(pluginDir, 'app'));
84
+ await fs.ensureDir(path.join(pluginDir, 'src'));
84
85
  await fs.ensureDir(path.join(pluginDir, 'client'));
85
86
  await fs.ensureDir(path.join(pluginDir, 'lib'));
86
87
  await fs.ensureDir(path.join(pluginDir, 'dist'));
@@ -104,23 +105,23 @@ async function createPluginPackage(pluginDir: string, pluginName: string, option
104
105
  },
105
106
  files: [
106
107
  'lib',
107
- 'app',
108
+ 'src',
108
109
  'dist',
109
110
  'client',
110
111
  'README.md',
111
112
  'CHANGELOG.md'
112
113
  ],
113
114
  scripts: {
114
- build: 'pnpm build:app && pnpm build:client',
115
- 'build:app': 'tsc --project tsconfig.app.json',
116
- 'build:client': 'tsc --project tsconfig.client.json',
117
- dev: 'tsc --project tsconfig.app.json --watch',
115
+ build: 'pnpm build:node && pnpm build:client',
116
+ 'build:node': 'tsc --project node.tsconfig.json',
117
+ 'build:client': 'zhin-console build',
118
+ dev: 'tsc --project node.tsconfig.json --watch',
118
119
  clean: 'rm -rf lib dist',
119
120
  prepublishOnly: 'pnpm build'
120
121
  },
121
122
  keywords: [
122
- 'zhin',
123
- 'zhin-plugin',
123
+ 'zhin.js',
124
+ 'plugin',
124
125
  pluginName
125
126
  ],
126
127
  author: '',
@@ -135,62 +136,77 @@ async function createPluginPackage(pluginDir: string, pluginName: string, option
135
136
  '@zhin.js/types': 'workspace:*',
136
137
  '@types/node': 'latest',
137
138
  '@types/react': 'latest',
139
+ '@types/react-dom': 'latest',
138
140
  'typescript': 'latest',
139
141
  'react': 'latest',
140
142
  'react-dom': 'latest',
141
- 'lucide-react': 'latest'
143
+ "@zhin.js/client":"workspace:*",
144
+ 'lucide-react': 'latest',
145
+ 'radix-ui': 'latest',
146
+ '@radix-ui/themes': 'latest'
142
147
  }
143
148
  };
144
149
 
145
150
  await fs.writeJson(path.join(pluginDir, 'package.json'), packageJson, { spaces: 2 });
146
151
 
147
- // 创建 tsconfig.app.json (插件代码)
148
- const tsConfigApp = {
149
- extends: '../../tsconfig.json',
150
- compilerOptions: {
151
- rootDir: './app',
152
- outDir: './lib',
153
- declaration: true,
154
- noEmit: false
152
+ // 创建 tsconfig.json (服务端主配置)
153
+ const tsConfig = {
154
+ "compilerOptions": {
155
+ "target": "ES2022",
156
+ "module": "ESNext",
157
+ "moduleResolution": "bundler",
158
+ "outDir": "./lib",
159
+ "rootDir": "./src",
160
+ "strict": true,
161
+ "esModuleInterop": true,
162
+ "skipLibCheck": true,
163
+ "forceConsistentCasingInFileNames": true,
164
+ "resolveJsonModule": true,
165
+ "isolatedModules": true,
166
+ "allowSyntheticDefaultImports": true,
167
+ "experimentalDecorators": true,
168
+ "emitDecoratorMetadata": true,
169
+ "declaration": true,
170
+ "declarationMap": true,
171
+ "sourceMap": true,
172
+ "verbatimModuleSyntax": false,
173
+ "types": [
174
+ "@zhin.js/http"
175
+ ]
155
176
  },
156
- include: ['app/**/*'],
157
- exclude: ['node_modules', 'lib', 'dist']
158
- };
177
+ "include": ["src/**/*"],
178
+ "exclude": ["lib", "node_modules", "client"]
179
+ }
180
+ ;
159
181
 
160
- await fs.writeJson(path.join(pluginDir, 'tsconfig.app.json'), tsConfigApp, { spaces: 2 });
161
-
162
- // 创建 tsconfig.client.json (客户端代码)
163
- const tsConfigClient = {
164
- compilerOptions: {
165
- target: 'ES2022',
166
- module: 'ESNext',
167
- moduleResolution: 'bundler',
168
- rootDir: './client',
169
- outDir: './dist',
170
- declaration: false,
171
- jsx: 'react-jsx',
172
- baseUrl: '.',
173
- skipLibCheck: true,
174
- esModuleInterop: true,
175
- allowSyntheticDefaultImports: true
176
- },
177
- include: ['client/**/*'],
178
- exclude: ['node_modules', 'lib', 'dist']
179
- };
182
+ await fs.writeJson(path.join(pluginDir, 'tsconfig.json'), tsConfig, { spaces: 2 });
180
183
 
181
- await fs.writeJson(path.join(pluginDir, 'tsconfig.client.json'), tsConfigClient, { spaces: 2 });
182
184
 
183
- // 创建 tsconfig.json (编辑器支持)
184
- const tsConfig = {
185
- extends: '../../tsconfig.json',
186
- include: ['app/**/*', 'client/**/*'],
187
- exclude: ['node_modules', 'lib', 'dist']
188
- };
185
+ // 创建 client/tsconfig.json (客户端主配置)
186
+ const clientTsConfig = {
187
+ "compilerOptions": {
188
+ "outDir": "../dist",
189
+ "baseUrl": ".",
190
+ "declaration": true,
191
+ "module": "ESNext",
192
+ "moduleResolution": "bundler",
193
+ "target": "ES2022",
194
+ "jsx":"react-jsx",
195
+ "declarationMap": true,
196
+ "sourceMap": true,
197
+ "skipLibCheck": true,
198
+ "noEmit": false
199
+ },
200
+ "include": [
201
+ "./**/*"
202
+ ]
203
+ }
204
+ ;
189
205
 
190
- await fs.writeJson(path.join(pluginDir, 'tsconfig.json'), tsConfig, { spaces: 2 });
206
+ await fs.writeJson(path.join(pluginDir, 'client', 'tsconfig.json'), clientTsConfig, { spaces: 2 });
191
207
 
192
- // 创建插件主入口文件 app/index.ts
193
- const appContent = `import {
208
+ // 创建服务端入口文件 src/index.ts
209
+ const indexContent = `import {
194
210
  useLogger,
195
211
  useContext,
196
212
  onDispose,
@@ -201,9 +217,10 @@ const logger = useLogger();
201
217
 
202
218
  // 注册客户端入口(如果有客户端代码)
203
219
  useContext('web', (web) => {
204
- const dispose = web.addEntry(
205
- path.resolve(import.meta.dirname, '../client/index.tsx')
206
- );
220
+ const dispose = web.addEntry({
221
+ production: path.resolve(import.meta.dirname, '../dist/index.js'),
222
+ development: path.resolve(import.meta.dirname, '../client/index.tsx')
223
+ });
207
224
  return dispose;
208
225
  });
209
226
 
@@ -215,7 +232,7 @@ onDispose(() => {
215
232
  logger.info('${capitalizedName} 插件已加载');
216
233
  `;
217
234
 
218
- await fs.writeFile(path.join(pluginDir, 'app', 'index.ts'), appContent);
235
+ await fs.writeFile(path.join(pluginDir, 'src', 'index.ts'), indexContent);
219
236
 
220
237
  // 创建客户端入口文件 client/index.tsx
221
238
  const clientContent = `import { addPage } from '@zhin.js/client';
@@ -235,20 +252,6 @@ export { ${capitalizedName}Page };
235
252
 
236
253
  await fs.writeFile(path.join(pluginDir, 'client', 'index.tsx'), clientContent);
237
254
 
238
- // 创建 client/tsconfig.json
239
- const clientTsConfig = {
240
- extends: '@zhin.js/console/browser.tsconfig.json',
241
- compilerOptions: {
242
- target: 'ES2022',
243
- module: 'ESNext',
244
- moduleResolution: 'bundler',
245
- jsx: 'react-jsx',
246
- baseUrl: '.'
247
- }
248
- };
249
-
250
- await fs.writeJson(path.join(pluginDir, 'client', 'tsconfig.json'), clientTsConfig, { spaces: 2 });
251
-
252
255
  // 创建客户端页面组件
253
256
  await fs.ensureDir(path.join(pluginDir, 'client', 'pages'));
254
257
  const pageContent = `import { useEffect } from 'react';
@@ -349,7 +352,7 @@ dist/
349
352
  }
350
353
  }
351
354
 
352
- async function addPluginToApp(pluginName: string) {
355
+ async function addPluginToApp(pluginName: string, isOfficial?: boolean) {
353
356
  try {
354
357
  const rootPackageJsonPath = path.resolve(process.cwd(), 'package.json');
355
358
 
@@ -360,7 +363,7 @@ async function addPluginToApp(pluginName: string) {
360
363
  }
361
364
 
362
365
  const packageJson = await fs.readJson(rootPackageJsonPath);
363
- const packageName = `@zhin.js/${pluginName}`;
366
+ const packageName = isOfficial ? `@zhin.js/${pluginName}` : `zhin.js-${pluginName}`;
364
367
 
365
368
  // 初始化 dependencies
366
369
  if (!packageJson.dependencies) {
@@ -0,0 +1,176 @@
1
+ import { Command } from 'commander';
2
+ import { logger } from '../utils/logger.js';
3
+ import fs from 'fs-extra';
4
+ import path from 'path';
5
+ import inquirer from 'inquirer';
6
+ import { execSync } from 'node:child_process';
7
+
8
+ interface PublishOptions {
9
+ tag?: string;
10
+ access?: 'public' | 'restricted';
11
+ registry?: string;
12
+ dryRun?: boolean;
13
+ skipBuild?: boolean;
14
+ }
15
+
16
+ export const pubCommand = new Command('pub')
17
+ .description('发布插件到 npm')
18
+ .argument('[plugin-name]', '插件名称(如: my-plugin)')
19
+ .option('--tag <tag>', '发布标签', 'latest')
20
+ .option('--access <access>', '访问级别 (public|restricted)', 'public')
21
+ .option('--registry <url>', '自定义 npm registry')
22
+ .option('--dry-run', '试运行,不实际发布', false)
23
+ .option('--skip-build', '跳过构建步骤', false)
24
+ .action(async (pluginName: string, options: PublishOptions) => {
25
+ try {
26
+ const pluginsDir = path.resolve(process.cwd(), 'plugins');
27
+
28
+ // 检查 plugins 目录是否存在
29
+ if (!fs.existsSync(pluginsDir)) {
30
+ logger.error('未找到 plugins 目录,请在项目根目录运行此命令');
31
+ process.exit(1);
32
+ }
33
+
34
+ // 获取所有插件
35
+ const availablePlugins = fs.readdirSync(pluginsDir)
36
+ .filter(name => {
37
+ const pluginPath = path.join(pluginsDir, name);
38
+ return fs.statSync(pluginPath).isDirectory() &&
39
+ fs.existsSync(path.join(pluginPath, 'package.json'));
40
+ });
41
+
42
+ if (availablePlugins.length === 0) {
43
+ logger.error('未找到可发布的插件');
44
+ process.exit(1);
45
+ }
46
+
47
+ // 如果没有指定插件名,让用户选择
48
+ let selectedPlugin = pluginName;
49
+ if (!selectedPlugin) {
50
+ if (availablePlugins.length === 1) {
51
+ selectedPlugin = availablePlugins[0];
52
+ logger.info(`自动选择插件: ${selectedPlugin}`);
53
+ } else {
54
+ const { plugin } = await inquirer.prompt([
55
+ {
56
+ type: 'list',
57
+ name: 'plugin',
58
+ message: '请选择要发布的插件:',
59
+ choices: availablePlugins
60
+ }
61
+ ]);
62
+ selectedPlugin = plugin;
63
+ }
64
+ }
65
+
66
+ // 验证插件是否存在
67
+ const pluginDir = path.join(pluginsDir, selectedPlugin);
68
+ if (!fs.existsSync(pluginDir)) {
69
+ logger.error(`插件不存在: ${selectedPlugin}`);
70
+ logger.log(`可用插件: ${availablePlugins.join(', ')}`);
71
+ process.exit(1);
72
+ }
73
+
74
+ const packageJsonPath = path.join(pluginDir, 'package.json');
75
+ if (!fs.existsSync(packageJsonPath)) {
76
+ logger.error(`未找到 package.json: ${packageJsonPath}`);
77
+ process.exit(1);
78
+ }
79
+
80
+ // 读取 package.json
81
+ const packageJson = await fs.readJson(packageJsonPath);
82
+ const packageName = packageJson.name;
83
+ const version = packageJson.version;
84
+
85
+ logger.info(`准备发布插件: ${packageName}@${version}`);
86
+ logger.log('');
87
+
88
+ // 确认发布
89
+ if (!options.dryRun) {
90
+ const { confirm } = await inquirer.prompt([
91
+ {
92
+ type: 'confirm',
93
+ name: 'confirm',
94
+ message: `确认发布 ${packageName}@${version} 到 npm?`,
95
+ default: false
96
+ }
97
+ ]);
98
+
99
+ if (!confirm) {
100
+ logger.warn('已取消发布');
101
+ process.exit(0);
102
+ }
103
+ }
104
+
105
+ // 构建插件
106
+ if (!options.skipBuild) {
107
+ logger.info('正在构建插件...');
108
+ try {
109
+ execSync('pnpm build', {
110
+ cwd: pluginDir,
111
+ stdio: 'inherit'
112
+ });
113
+ logger.success('✓ 构建完成');
114
+ } catch (error) {
115
+ logger.error('构建失败');
116
+ throw error;
117
+ }
118
+ }
119
+
120
+ // 构建 npm publish 命令
121
+ const publishArgs = ['publish'];
122
+
123
+ if (options.access) {
124
+ publishArgs.push('--access', options.access);
125
+ }
126
+
127
+ if (options.tag) {
128
+ publishArgs.push('--tag', options.tag);
129
+ }
130
+
131
+ if (options.registry) {
132
+ publishArgs.push('--registry', options.registry);
133
+ }
134
+
135
+ if (options.dryRun) {
136
+ publishArgs.push('--dry-run');
137
+ }
138
+
139
+ // 总是添加 --no-git-checks(因为 plugins 可能不是 git 根目录)
140
+ publishArgs.push('--no-git-checks');
141
+
142
+ // 发布插件
143
+ logger.info(`正在发布${options.dryRun ? '(试运行)' : ''}...`);
144
+ logger.log(`命令: pnpm ${publishArgs.join(' ')}`);
145
+ logger.log('');
146
+
147
+ try {
148
+ execSync(`pnpm ${publishArgs.join(' ')}`, {
149
+ cwd: pluginDir,
150
+ stdio: 'inherit'
151
+ });
152
+
153
+ if (options.dryRun) {
154
+ logger.success('✓ 试运行完成');
155
+ logger.log('');
156
+ logger.log('💡 提示: 移除 --dry-run 参数以实际发布');
157
+ } else {
158
+ logger.success(`✓ ${packageName}@${version} 发布成功!`);
159
+ logger.log('');
160
+ logger.log('📦 安装命令:');
161
+ logger.log(` pnpm add ${packageName}`);
162
+ logger.log('');
163
+ logger.log('🔗 npm 链接:');
164
+ logger.log(` https://www.npmjs.com/package/${packageName}`);
165
+ }
166
+ } catch (error) {
167
+ logger.error('发布失败');
168
+ throw error;
169
+ }
170
+
171
+ } catch (error: any) {
172
+ logger.error(`发布失败: ${error.message}`);
173
+ process.exit(1);
174
+ }
175
+ });
176
+
@@ -0,0 +1,243 @@
1
+ import { Command } from 'commander';
2
+ import { logger } from '../utils/logger.js';
3
+ import { execSync } from 'node:child_process';
4
+
5
+ interface SearchOptions {
6
+ category?: string;
7
+ limit?: number;
8
+ official?: boolean;
9
+ }
10
+
11
+ export const searchCommand = new Command('search')
12
+ .description('搜索 Zhin.js 插件')
13
+ .argument('[keyword]', '搜索关键词')
14
+ .option('-c, --category <category>', '按分类搜索 (utility|service|game|adapter|admin|ai)')
15
+ .option('-l, --limit <number>', '限制结果数量', '20')
16
+ .option('--official', '仅显示官方插件', false)
17
+ .action(async (keyword: string, options: SearchOptions) => {
18
+ try {
19
+ logger.info('正在搜索插件...');
20
+ logger.log('');
21
+
22
+ // 构建搜索查询
23
+ let searchQuery = 'zhin';
24
+
25
+ if (options.official) {
26
+ searchQuery = '@zhin.js';
27
+ } else if (keyword) {
28
+ searchQuery = `zhin.js ${keyword} `;
29
+ } else {
30
+ searchQuery = 'zhin.js plugin';
31
+ }
32
+
33
+ // 使用 npm search
34
+ const cmd = `npm search ${searchQuery} --json`;
35
+
36
+ try {
37
+ const output = execSync(cmd, {
38
+ encoding: 'utf-8',
39
+ maxBuffer: 10 * 1024 * 1024, // 10MB
40
+ stdio: ['pipe', 'pipe', 'ignore'] // 忽略 stderr
41
+ });
42
+
43
+ const results = JSON.parse(output);
44
+
45
+ // 过滤结果
46
+ let filteredResults = results.filter((pkg: any) => {
47
+ // 必须包含 zhin 关键词
48
+ const keywords = pkg.keywords || [];
49
+ const hasZhin = keywords.includes('zhin') ||
50
+ keywords.includes('plugin') ||
51
+ pkg.name.startsWith('@zhin.js/') ||
52
+ pkg.name.startsWith('zhin.js-');
53
+
54
+ if (!hasZhin) return false;
55
+
56
+ // 按分类过滤
57
+ if (options.category) {
58
+ const category = pkg.keywords?.find((k: string) =>
59
+ k.includes(options.category!)
60
+ );
61
+ if (!category) return false;
62
+ }
63
+
64
+ // 按关键词过滤
65
+ if (keyword) {
66
+ const searchIn = [
67
+ pkg.name,
68
+ pkg.description || '',
69
+ ...(pkg.keywords || [])
70
+ ].join(' ').toLowerCase();
71
+
72
+ return searchIn.includes(keyword.toLowerCase());
73
+ }
74
+
75
+ return true;
76
+ });
77
+
78
+ // 限制结果数量
79
+ const limit = parseInt(String(options.limit) || '20');
80
+ if (filteredResults.length > limit) {
81
+ filteredResults = filteredResults.slice(0, limit);
82
+ }
83
+
84
+ // 显示结果
85
+ if (filteredResults.length === 0) {
86
+ logger.warn('未找到匹配的插件');
87
+ logger.log('');
88
+ logger.log('💡 提示:');
89
+ logger.log(' - 尝试使用不同的关键词');
90
+ logger.log(' - 访问插件市场: https://zhin.pages.dev/plugins');
91
+ logger.log(' - 在 GitHub 搜索: https://github.com/topics/zhin.js');
92
+ return;
93
+ }
94
+
95
+ logger.success(`找到 ${filteredResults.length} 个插件:`);
96
+ logger.log('');
97
+
98
+ // 按下载量排序
99
+ filteredResults.sort((a: any, b: any) => {
100
+ const aDownloads = parseInt(a.downloads || '0');
101
+ const bDownloads = parseInt(b.downloads || '0');
102
+ return bDownloads - aDownloads;
103
+ });
104
+
105
+ // 显示插件列表
106
+ filteredResults.forEach((pkg: any, index: number) => {
107
+ const name = pkg.name;
108
+ const version = pkg.version;
109
+ const description = pkg.description || '无描述';
110
+ const author = pkg.publisher?.username || '未知';
111
+ const date = pkg.date ? new Date(pkg.date).toLocaleDateString('zh-CN') : '未知';
112
+
113
+ // 判断插件类型
114
+ let badge = '';
115
+ if (name.startsWith('@zhin.js/')) {
116
+ badge = '✨ [官方]';
117
+ } else if (name.startsWith('zhin.js-')) {
118
+ badge = '📦 [社区]';
119
+ }
120
+
121
+ logger.log(`${index + 1}. ${badge} ${name}@${version}`);
122
+ logger.log(` ${description}`);
123
+ logger.log(` 作者: ${author} | 更新: ${date}`);
124
+
125
+ // 显示安装命令
126
+ logger.log(` 安装: zhin install ${name}`);
127
+ logger.log('');
128
+ });
129
+
130
+ logger.log('💡 提示:');
131
+ logger.log(' - 使用 zhin info <package> 查看插件详情');
132
+ logger.log(' - 使用 zhin install <package> 安装插件');
133
+ logger.log(' - 访问 https://zhin.pages.dev/plugins 查看完整列表');
134
+
135
+ } catch (error) {
136
+ logger.error('搜索失败,请检查网络连接');
137
+ logger.log('');
138
+ logger.log('💡 替代方案:');
139
+ logger.log(' - 访问 npm: https://www.npmjs.com/search?q=zhin.js');
140
+ logger.log(' - 访问 GitHub: https://github.com/topics/zhin.js');
141
+ throw error;
142
+ }
143
+
144
+ } catch (error: any) {
145
+ logger.error(`搜索插件失败: ${error.message}`);
146
+ process.exit(1);
147
+ }
148
+ });
149
+
150
+ export const infoCommand = new Command('info')
151
+ .description('查看插件详细信息')
152
+ .argument('<package>', '插件包名')
153
+ .action(async (packageName: string) => {
154
+ try {
155
+ logger.info(`正在获取 ${packageName} 的信息...`);
156
+ logger.log('');
157
+
158
+ const cmd = `npm view ${packageName} --json`;
159
+
160
+ try {
161
+ const output = execSync(cmd, {
162
+ encoding: 'utf-8',
163
+ stdio: ['pipe', 'pipe', 'ignore']
164
+ });
165
+
166
+ const info = JSON.parse(output);
167
+
168
+ // 显示插件信息
169
+ logger.success('插件信息:');
170
+ logger.log('');
171
+
172
+ logger.log(`📦 名称: ${info.name}`);
173
+ logger.log(`📝 版本: ${info.version}`);
174
+ logger.log(`📄 描述: ${info.description || '无'}`);
175
+ logger.log(`👤 作者: ${info.author?.name || info.maintainers?.[0]?.name || '未知'}`);
176
+ logger.log(`📅 发布时间: ${new Date(info.time?.modified || info.time?.created).toLocaleDateString('zh-CN')}`);
177
+
178
+ if (info.keywords?.length > 0) {
179
+ logger.log(`🏷️ 标签: ${info.keywords.join(', ')}`);
180
+ }
181
+
182
+ if (info.homepage) {
183
+ logger.log(`🏠 主页: ${info.homepage}`);
184
+ }
185
+
186
+ if (info.repository?.url) {
187
+ logger.log(`📂 仓库: ${info.repository.url.replace(/^git\+/, '').replace(/\.git$/, '')}`);
188
+ }
189
+
190
+ if (info.bugs?.url) {
191
+ logger.log(`🐛 问题: ${info.bugs.url}`);
192
+ }
193
+
194
+ if (info.license) {
195
+ logger.log(`⚖️ 许可: ${info.license}`);
196
+ }
197
+
198
+ // 显示依赖
199
+ if (info.peerDependencies) {
200
+ logger.log('');
201
+ logger.log('🔗 对等依赖:');
202
+ Object.entries(info.peerDependencies).forEach(([dep, ver]) => {
203
+ logger.log(` ${dep}: ${ver}`);
204
+ });
205
+ }
206
+
207
+ // 显示安装命令
208
+ logger.log('');
209
+ logger.log('📥 安装命令:');
210
+ logger.log(` zhin install ${info.name}`);
211
+ logger.log(` # 或`);
212
+ logger.log(` pnpm add ${info.name}`);
213
+
214
+ // Zhin 特定信息
215
+ if (info.zhin) {
216
+ logger.log('');
217
+ logger.log('🎯 Zhin 信息:');
218
+ if (info.zhin.displayName) {
219
+ logger.log(` 显示名称: ${info.zhin.displayName}`);
220
+ }
221
+ if (info.zhin.category) {
222
+ logger.log(` 分类: ${info.zhin.category}`);
223
+ }
224
+ if (info.zhin.features?.length > 0) {
225
+ logger.log(` 功能: ${info.zhin.features.join(', ')}`);
226
+ }
227
+ }
228
+
229
+ } catch (error) {
230
+ logger.error(`未找到插件: ${packageName}`);
231
+ logger.log('');
232
+ logger.log('💡 提示:');
233
+ logger.log(' - 检查插件名称是否正确');
234
+ logger.log(' - 使用 zhin search 搜索插件');
235
+ throw error;
236
+ }
237
+
238
+ } catch (error: any) {
239
+ logger.error(`获取插件信息失败: ${error.message}`);
240
+ process.exit(1);
241
+ }
242
+ });
243
+