mirror of
https://github.com/antvis/chart-visualization-skills.git
synced 2026-09-14 15:58:00 +08:00
chore: 完善 CLI 逻辑 (#70)
* chore: 完善 CLI 逻辑 * chore: 新增 g6 bm25 检索词 * chore: 异常捕获优化 * chore: --json 修改为 --output
This commit is contained in:
@@ -96,11 +96,20 @@ npm install -g @antv/chart-visualization-skills
|
||||
# Retrieve skills by query
|
||||
antv retrieve "bar chart" --library g2 --topk 10 --content
|
||||
|
||||
# Retrieve skills and output as JSON
|
||||
antv retrieve "bar chart" --library g2 --output json
|
||||
|
||||
# List all available skills
|
||||
antv list --library g2 --category core
|
||||
|
||||
# List skills and output as JSON
|
||||
antv list --output json
|
||||
|
||||
# Show skill info
|
||||
antv info --library g2
|
||||
|
||||
# Show skill info as JSON
|
||||
antv info --library g2 --output json
|
||||
```
|
||||
|
||||
**Usage for the command**:
|
||||
@@ -116,9 +125,13 @@ Options:
|
||||
|
||||
Commands:
|
||||
retrieve [options] <query> Search for skills matching a query
|
||||
get [options] <id> Get a skill by its exact ID
|
||||
list [options] List all available skills
|
||||
info [options] Show skill info from SKILL.md
|
||||
help [command] display help for command
|
||||
|
||||
Options shared by all commands:
|
||||
--output <format> Output format: json | text (default: "text")
|
||||
```
|
||||
|
||||
### API Usage
|
||||
|
||||
+52
-13
@@ -1,30 +1,69 @@
|
||||
import { retrieve as _retrieve, getSkillInfo } from './core/retriever';
|
||||
import type { Skill, SkillInfo } from './core/types';
|
||||
|
||||
export type { Skill, SkillInfo };
|
||||
import {
|
||||
retrieve as _retrieve,
|
||||
getSkillById as _getSkillById,
|
||||
getSkillInfo,
|
||||
availableLibraries
|
||||
} from './core/retriever';
|
||||
import type { Skill, SkillInfo, RetrieveOptions } from './core/types';
|
||||
|
||||
export type { Skill, SkillInfo, RetrieveOptions };
|
||||
|
||||
/**
|
||||
* Retrieve skills based on a query.
|
||||
*
|
||||
* @param query The search query for skills.
|
||||
* @param library The library to search within (default: 'g2').
|
||||
* @param topk The number of top results to return (default: 7).
|
||||
* @param content Whether to include markdown content body (without frontmatter) of each skill (default: false).
|
||||
* @example retrieve('bar chart', 'g2', 1);
|
||||
* @returns An array of skills matching the query.
|
||||
* Preferred: pass an options object.
|
||||
* @example retrieve('bar chart', { library: 'g2', topK: 5, content: true })
|
||||
*
|
||||
* Legacy positional signature still supported for backwards compatibility.
|
||||
* @example retrieve('bar chart', 'g2', 5, true)
|
||||
*/
|
||||
export function retrieve(query: string, library = 'g2', topk = 7, content = false): Skill[] {
|
||||
return _retrieve(query, { library, topK: topk, content });
|
||||
export function retrieve(query: string, options?: RetrieveOptions): Skill[];
|
||||
/** @deprecated Use the options-object overload instead. */
|
||||
export function retrieve(
|
||||
query: string,
|
||||
library?: string,
|
||||
topk?: number,
|
||||
content?: boolean
|
||||
): Skill[];
|
||||
export function retrieve(
|
||||
query: string,
|
||||
libraryOrOpts?: string | RetrieveOptions,
|
||||
topk = 7,
|
||||
content = false
|
||||
): Skill[] {
|
||||
if (typeof libraryOrOpts === 'string' || libraryOrOpts === undefined) {
|
||||
return _retrieve(query, { library: libraryOrOpts, topK: topk, content });
|
||||
}
|
||||
return _retrieve(query, libraryOrOpts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single skill by its exact ID.
|
||||
*
|
||||
* @param id The skill ID (e.g. 'g2-mark-bar').
|
||||
* @param library Optional: restrict the search to a specific library.
|
||||
* @returns The skill with full content, or undefined if not found.
|
||||
* @example getSkillById('g2-mark-bar')
|
||||
*/
|
||||
export function getSkillById(id: string, library?: string): Skill | undefined {
|
||||
return _getSkillById(id, library);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get skill info embedded in the library index.
|
||||
*
|
||||
* @param library The library to get info for (default: 'g2').
|
||||
* @example info('g2');
|
||||
* @example info('g2')
|
||||
* @returns The skill info, or undefined if not available.
|
||||
*/
|
||||
export function info(library = 'g2'): SkillInfo | undefined {
|
||||
return getSkillInfo(library);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of libraries that have a built index on disk.
|
||||
* @example libraries() // ['g2', 'g6']
|
||||
*/
|
||||
export function libraries(): string[] {
|
||||
return availableLibraries();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import { Command } from 'commander';
|
||||
import { getSkillById } from '../core/retriever';
|
||||
|
||||
export function registerGetCommand(program: Command): void {
|
||||
program
|
||||
.command('get <id>')
|
||||
.description('Get a skill by its exact ID')
|
||||
.option('--library <lib>', 'Restrict search to a specific library')
|
||||
.option('--output <format>', 'Output format: json | text', 'text')
|
||||
.action((id: string, opts: { library?: string; output: string }) => {
|
||||
const skill = getSkillById(id, opts.library);
|
||||
|
||||
if (!skill) {
|
||||
const hint = opts.library ? ` in library "${opts.library}"` : '';
|
||||
console.error(`Skill not found: "${id}"${hint}`);
|
||||
console.error('Tip: run `antv list` to browse available skill IDs.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (opts.output === 'json') {
|
||||
console.log(JSON.stringify(skill, null, 2));
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`${'─'.repeat(50)}`);
|
||||
console.log(`${skill.title} (${skill.id})`);
|
||||
console.log(`Library : ${skill.library} v${skill.version}`);
|
||||
console.log(
|
||||
`Category : ${skill.category}${skill.subcategory ? '/' + skill.subcategory : ''}`
|
||||
);
|
||||
console.log(`Tags : ${skill.tags.join(', ')}`);
|
||||
console.log(`Desc : ${skill.description}`);
|
||||
if (skill.use_cases.length)
|
||||
console.log(`Cases : ${skill.use_cases.join(' / ')}`);
|
||||
if (skill.anti_patterns.length)
|
||||
console.log(`Avoid : ${skill.anti_patterns.join(' / ')}`);
|
||||
if (skill.related.length)
|
||||
console.log(`Related : ${skill.related.join(', ')}`);
|
||||
if (skill.content) {
|
||||
console.log(`\n${skill.content}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -6,11 +6,17 @@ export function registerInfoCommand(program: Command): void {
|
||||
.command('info')
|
||||
.description('Show skill info from SKILL.md')
|
||||
.option('--library <lib>', 'Library to show info for (g2 or g6)', 'g2')
|
||||
.action((opts: { library: string }) => {
|
||||
.option('--output <format>', 'Output format: json | text', 'text')
|
||||
.action((opts: { library: string; output: string }) => {
|
||||
const skill = getSkillInfo(opts.library);
|
||||
|
||||
if (!skill) {
|
||||
console.log(`No skill info found for library: ${opts.library}`);
|
||||
console.error(`No skill info found for library: ${opts.library}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (opts.output === 'json') {
|
||||
console.log(JSON.stringify(skill, null, 2));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+40
-22
@@ -9,30 +9,48 @@ export function registerListCommand(program: Command): void {
|
||||
.option('--library <lib>', 'Filter by library (g2 or g6)')
|
||||
.option('--category <cat>', 'Filter by category')
|
||||
.option('--tags <tags>', 'Filter by tags (comma-separated)')
|
||||
.option('--difficulty <level>', 'Filter by difficulty (beginner|intermediate|advanced)')
|
||||
.action((opts: { library?: string; category?: string; tags?: string; difficulty?: string }) => {
|
||||
const skills = listSkills({
|
||||
library: opts.library,
|
||||
category: opts.category || null,
|
||||
tags: opts.tags ? opts.tags.split(',').map(t => t.trim()) : [],
|
||||
difficulty: opts.difficulty || null,
|
||||
});
|
||||
.option(
|
||||
'--difficulty <level>',
|
||||
'Filter by difficulty (beginner|intermediate|advanced)'
|
||||
)
|
||||
.option('--output <format>', 'Output format: json | text', 'text')
|
||||
.action(
|
||||
(opts: {
|
||||
library?: string;
|
||||
category?: string;
|
||||
tags?: string;
|
||||
difficulty?: string;
|
||||
output: string;
|
||||
}) => {
|
||||
const skills = listSkills({
|
||||
library: opts.library,
|
||||
category: opts.category || null,
|
||||
tags: opts.tags ? opts.tags.split(',').map((t) => t.trim()) : [],
|
||||
difficulty: opts.difficulty || null
|
||||
});
|
||||
|
||||
const summary = `Total skills found: ${skills.length}`;
|
||||
|
||||
const groupedByLibrary: Record<string, Skill[]> = skills.reduce((acc: Record<string, Skill[]>, skill) => {
|
||||
if (!acc[skill.library]) {
|
||||
acc[skill.library] = [];
|
||||
if (opts.output === 'json') {
|
||||
console.log(JSON.stringify(skills, null, 2));
|
||||
return;
|
||||
}
|
||||
acc[skill.library].push(skill);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const content = Object.entries(groupedByLibrary).map(([lib, libSkills]) => {
|
||||
const skillList = libSkills.map(skill => ` - ${skill.id}: ${skill.title}`).join('\n');
|
||||
return `${lib.toUpperCase()}, ${libSkills.length} documents found:\n${skillList}`;
|
||||
}).join('\n\n');
|
||||
const groupedByLibrary: Record<string, Skill[]> = skills.reduce(
|
||||
(acc: Record<string, Skill[]>, skill) => {
|
||||
if (!acc[skill.library]) acc[skill.library] = [];
|
||||
acc[skill.library].push(skill);
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
console.log(`${summary}\n\n${content}`);
|
||||
});
|
||||
console.log(`Total skills found: ${skills.length}\n`);
|
||||
for (const [lib, libSkills] of Object.entries(groupedByLibrary)) {
|
||||
console.log(`${lib.toUpperCase()} (${libSkills.length} skills)`);
|
||||
for (const skill of libSkills) {
|
||||
console.log(` ${skill.id.padEnd(48)} ${skill.title}`);
|
||||
}
|
||||
console.log();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
+40
-20
@@ -8,26 +8,46 @@ export function registerRetrieveCommand(program: Command): void {
|
||||
.option('--library <lib>', 'Filter by library (g2 or g6)')
|
||||
.option('--topk <n>', 'Number of results to return', '7')
|
||||
.option('--content', 'Include markdown content body')
|
||||
.action((query: string, opts: { library?: string; topk: string; content?: true }) => {
|
||||
const topK = parseInt(opts.topk, 10) || 7;
|
||||
const skills = retrieve(query, { library: opts.library, topK, content: !!opts.content });
|
||||
.option('--output <format>', 'Output format: json | text', 'text')
|
||||
.action(
|
||||
(
|
||||
query: string,
|
||||
opts: { library?: string; topk: string; content?: true; output: string }
|
||||
) => {
|
||||
const topK = parseInt(opts.topk, 10) || 7;
|
||||
const skills = retrieve(query, {
|
||||
library: opts.library,
|
||||
topK,
|
||||
content: !!opts.content
|
||||
});
|
||||
|
||||
if (skills.length === 0) {
|
||||
console.log('No skills found.');
|
||||
return;
|
||||
if (opts.output === 'json') {
|
||||
console.log(JSON.stringify(skills, null, 2));
|
||||
return;
|
||||
}
|
||||
|
||||
if (skills.length === 0) {
|
||||
console.log('No skills found.');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Total ${skills.length} documents found:`);
|
||||
for (const [i, skill] of skills.entries()) {
|
||||
console.log(`\n${'─'.repeat(50)}`);
|
||||
console.log(`[${i + 1}] ${skill.title} (${skill.id})`);
|
||||
console.log(
|
||||
` Category : ${skill.category}${skill.subcategory ? '/' + skill.subcategory : ''}`
|
||||
);
|
||||
console.log(` Tags : ${skill.tags.join(', ')}`);
|
||||
console.log(` Desc : ${skill.description}`);
|
||||
if (skill.use_cases.length)
|
||||
console.log(` Cases : ${skill.use_cases.join(' / ')}`);
|
||||
if (skill.anti_patterns.length)
|
||||
console.log(` Avoid : ${skill.anti_patterns.join(' / ')}`);
|
||||
if (skill.content) {
|
||||
console.log(`\n${skill.content}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const summary = `Total ${skills.length} documents found:`;
|
||||
const content = skills.map((skill, i) => `
|
||||
${i + 1}. ${skill.title}
|
||||
ID: ${skill.id}
|
||||
Category: ${skill.category}${skill.subcategory ? '/' + skill.subcategory : ''}
|
||||
Tags: ${skill.tags.join(', ')}
|
||||
Description: ${skill.description}
|
||||
Content: ${skill.content ?? '' }
|
||||
Use Cases: ${skill.use_cases.join(', ')}
|
||||
Anti Patterns: ${skill.anti_patterns.join(', ')}`.trim());
|
||||
|
||||
console.log(`${summary}\n${content.join('\n')}`);
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ const STOP_WORDS = new Set([
|
||||
]);
|
||||
|
||||
const PRIMARY_CHART_TOKENS = new Set([
|
||||
// G2 statistical chart types
|
||||
'beeswarm', 'sankey', 'chord', 'treemap', 'sunburst', 'boxplot', 'waterfall', 'funnel',
|
||||
'gauge', 'gantt', 'wordcloud', 'candlestick', 'bullet', 'density', 'liquid', 'venn',
|
||||
'pack', 'spiral', 'contour', 'violin', 'ridgeline', 'marimekko', 'mosaic', 'bump',
|
||||
@@ -38,6 +39,10 @@ const PRIMARY_CHART_TOKENS = new Set([
|
||||
'蜂群图', '漏斗图', '玫瑰图', '仪表盘', '甘特图', '词云', '箱线图', '旭日图',
|
||||
'矩形树图', '桑基图', '和弦图', '密度图', '打包图', '瀑布图', 'K线图', '子弹图',
|
||||
'韦恩图', '液体图', '螺旋图', '小提琴图',
|
||||
// G6 graph types and layouts
|
||||
'dagre', 'fishbone', 'mindmap', 'radial', 'dendrogram',
|
||||
'关系图', '网络图', '拓扑图', '流程图', '思维导图', '鱼骨图', '组织架构图', '知识图谱',
|
||||
'层次图', '辐射图',
|
||||
]);
|
||||
|
||||
const SYNONYMS = new Map<string, string[]>([
|
||||
@@ -74,6 +79,28 @@ const SYNONYMS = new Map<string, string[]>([
|
||||
['暗色', ['theme', 'dark']], ['深色', ['theme', 'dark']],
|
||||
['动画', ['animation', 'animate']], ['交互', ['interaction']],
|
||||
['框选', ['brush']], ['高亮', ['highlight', 'elementhighlight']],
|
||||
// G6: Chinese → English
|
||||
['关系图', ['network', 'graph']], ['网络图', ['network', 'graph']],
|
||||
['拓扑图', ['network', 'topology']], ['知识图谱', ['network', 'knowledge']],
|
||||
['流程图', ['flow', 'dag', 'dagre']], ['有向无环图', ['dag', 'dagre']],
|
||||
['思维导图', ['mindmap']], ['组织架构图', ['tree', 'dendrogram']],
|
||||
['鱼骨图', ['fishbone']], ['层次图', ['dagre', 'hierarchy']],
|
||||
['辐射图', ['radial']], ['树状图', ['tree', 'dendrogram']],
|
||||
['力导向', ['force']], ['力导向布局', ['force']],
|
||||
['层次布局', ['dagre', 'hierarchy']], ['环形布局', ['circular']],
|
||||
['辐射布局', ['radial']], ['网格布局', ['grid']],
|
||||
['节点', ['node']], ['连线', ['edge', 'link']], ['组合', ['combo']],
|
||||
['套索', ['lasso']], ['折叠', ['collapse']], ['展开', ['expand']],
|
||||
['缩略图', ['minimap']], ['时间轴', ['timebar']], ['工具栏', ['toolbar']],
|
||||
['拖拽画布', ['drag-canvas']], ['缩放画布', ['zoom-canvas']],
|
||||
['点击选中', ['click-select']], ['拖拽元素', ['drag-element']],
|
||||
// G6: English → Chinese
|
||||
['dagre', ['流程图', '层次']], ['fishbone', ['鱼骨图']],
|
||||
['mindmap', ['思维导图']], ['radial', ['辐射']],
|
||||
['dendrogram', ['树状图', '组织架构']],
|
||||
['node', ['节点']], ['combo', ['组合']],
|
||||
['minimap', ['缩略图']], ['timebar', ['时间轴']], ['toolbar', ['工具栏']],
|
||||
['lasso', ['套索', '框选']], ['collapse', ['折叠']], ['expand', ['展开']],
|
||||
['line', ['折线']], ['bar', ['柱状']], ['pie', ['饼图']],
|
||||
['interval', ['柱状']], ['scatter', ['散点']], ['point', ['散点']],
|
||||
['area', ['面积']], ['heatmap', ['热力']], ['cell', ['热力']],
|
||||
@@ -88,6 +115,7 @@ const SYNONYMS = new Map<string, string[]>([
|
||||
]);
|
||||
|
||||
const EXTRA_DICT = new Set([
|
||||
// common interaction/config
|
||||
'点击', '拖拽', '缩放', '悬停', '选中', '过滤',
|
||||
'渲染', '更新', '刷新', '加载', '切换', '联动',
|
||||
'指标', '目标', '数值', '百分比', '进度', '占比',
|
||||
@@ -96,6 +124,10 @@ const EXTRA_DICT = new Set([
|
||||
'布局', '容器', '宽度', '高度', '间距', '边距',
|
||||
'颜色', '透明度', '圆角', '虚线', '实线',
|
||||
'字体', '字号', '粗细', '旋转', '偏移',
|
||||
// G6-specific
|
||||
'节点', '连线', '组合', '套索', '折叠', '展开',
|
||||
'关系', '拓扑', '层次', '辐射', '力导向',
|
||||
'缩略图', '时间轴', '工具栏', '画布', '边框',
|
||||
]);
|
||||
|
||||
const _DICT_TERMS = [
|
||||
|
||||
+59
-13
@@ -10,6 +10,18 @@ const DEFAULT_LIBRARY = 'g2';
|
||||
|
||||
const bm25Cache = new Map<string, BM25Index>();
|
||||
|
||||
/**
|
||||
* Return the list of libraries that have a built index on disk.
|
||||
*/
|
||||
export function availableLibraries(): string[] {
|
||||
if (!fs.existsSync(DEFAULT_INDEX_DIR)) return [];
|
||||
return fs
|
||||
.readdirSync(DEFAULT_INDEX_DIR)
|
||||
.filter((f) => f.endsWith('.index.json'))
|
||||
.map((f) => f.replace('.index.json', ''))
|
||||
.sort();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the index JSON file.
|
||||
* @param library The library name.
|
||||
@@ -19,7 +31,13 @@ export function loadIndex(library: string): SkillIndex {
|
||||
const indexFile = path.join(DEFAULT_INDEX_DIR, `${library}.index.json`);
|
||||
|
||||
if (!fs.existsSync(indexFile)) {
|
||||
throw new Error(`Index file not found: ${indexFile}. Run build first.`);
|
||||
// Only scan the directory on the error path to build a helpful message.
|
||||
const libs = availableLibraries();
|
||||
throw new Error(
|
||||
libs.length > 0
|
||||
? `Unknown library: "${library}". Available: ${libs.join(', ')}`
|
||||
: `Index file not found for "${library}". Run build first.`
|
||||
);
|
||||
}
|
||||
|
||||
return JSON.parse(fs.readFileSync(indexFile, 'utf-8'));
|
||||
@@ -47,10 +65,24 @@ function getBM25Index(library: string): BM25Index {
|
||||
* @param options Options to customize the retrieval.
|
||||
* @returns An array of skills matching the query.
|
||||
*/
|
||||
export function retrieve(query: string, options: RetrieveOptions = {}): Skill[] {
|
||||
const { library = DEFAULT_LIBRARY, topK = 7, content = false } = options;
|
||||
const index = getBM25Index(library);
|
||||
const skills = index.search(query, topK).map(({ skill }) => skill);
|
||||
export function retrieve(
|
||||
query: string,
|
||||
options: RetrieveOptions = {}
|
||||
): Skill[] {
|
||||
const { library, topK = 7, content = false } = options;
|
||||
|
||||
let skills: Skill[];
|
||||
if (library) {
|
||||
skills = getBM25Index(library)
|
||||
.search(query, topK)
|
||||
.map((r) => r.skill);
|
||||
} else {
|
||||
skills = availableLibraries()
|
||||
.flatMap((lib) => getBM25Index(lib).search(query, topK))
|
||||
.sort((a, b) => b.score - a.score)
|
||||
.slice(0, topK)
|
||||
.map((r) => r.skill);
|
||||
}
|
||||
|
||||
if (!content) {
|
||||
return skills.map(({ content, ...skill }) => skill);
|
||||
@@ -68,21 +100,35 @@ export function getSkillInfo(library = DEFAULT_LIBRARY): SkillIndex['info'] {
|
||||
return loadIndex(library).info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single skill by its exact ID, searching across all available libraries
|
||||
* unless a specific library is provided.
|
||||
* @param id The skill ID.
|
||||
* @param library Optional library to restrict the search.
|
||||
* @returns The skill (with content), or undefined if not found.
|
||||
*/
|
||||
export function getSkillById(id: string, library?: string): Skill | undefined {
|
||||
const libs = library ? [library] : availableLibraries();
|
||||
for (const lib of libs) {
|
||||
const { skills } = loadIndex(lib);
|
||||
const found = skills.find((s) => s.id === id);
|
||||
if (found) return found;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* List all the skills, optionally filtered by library, category, tags, or difficulty.
|
||||
* @param options Options to filter the skills.
|
||||
* @returns An array of skills matching the filters.
|
||||
*/
|
||||
export function listSkills(options: ListOptions = {}): Skill[] {
|
||||
const {
|
||||
library = DEFAULT_LIBRARY,
|
||||
category = null,
|
||||
tags = [],
|
||||
difficulty = null
|
||||
} = options;
|
||||
const { skills } = loadIndex(library);
|
||||
const { library, category = null, tags = [], difficulty = null } = options;
|
||||
|
||||
return skills.filter((skill) => {
|
||||
const libs = library ? [library] : availableLibraries();
|
||||
const allSkills = libs.flatMap((lib) => loadIndex(lib).skills);
|
||||
|
||||
return allSkills.filter((skill) => {
|
||||
if (category && skill.category !== category) return false;
|
||||
if (difficulty && skill.difficulty !== difficulty) return false;
|
||||
if (tags.length > 0 && !tags.some((t) => skill.tags.includes(t)))
|
||||
|
||||
+19
-2
@@ -5,6 +5,7 @@ import path from 'path';
|
||||
import { registerRetrieveCommand } from './commands/retrieve';
|
||||
import { registerListCommand } from './commands/list';
|
||||
import { registerInfoCommand } from './commands/info';
|
||||
import { registerGetCommand } from './commands/get';
|
||||
|
||||
const pkg = require(path.resolve(__dirname, '../package.json'));
|
||||
|
||||
@@ -13,10 +14,26 @@ const program = new Command();
|
||||
program
|
||||
.name('antv')
|
||||
.description('CLI tool for AntV chart visualization skills retrieval')
|
||||
.version(pkg.version);
|
||||
.version(pkg.version)
|
||||
.option('--debug', 'Show full stack trace on error');
|
||||
|
||||
registerRetrieveCommand(program);
|
||||
registerGetCommand(program);
|
||||
registerListCommand(program);
|
||||
registerInfoCommand(program);
|
||||
|
||||
program.parse();
|
||||
// Wrap parse() so errors thrown inside synchronous action handlers are caught
|
||||
// here rather than relying on the global uncaughtException hook, which would
|
||||
// also swallow unexpected programming errors (TypeError, ReferenceError, etc.).
|
||||
// Pass --debug to see the full stack when debugging unexpected failures.
|
||||
try {
|
||||
program.parse();
|
||||
} catch (err) {
|
||||
const debug = process.argv.includes('--debug');
|
||||
if (debug) {
|
||||
console.error(err);
|
||||
} else {
|
||||
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user