@open-skills-hub/cli 1.0.0

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.
Files changed (57) hide show
  1. package/dist/commands/cache.d.ts +6 -0
  2. package/dist/commands/cache.d.ts.map +1 -0
  3. package/dist/commands/cache.js +145 -0
  4. package/dist/commands/cache.js.map +1 -0
  5. package/dist/commands/config.d.ts +6 -0
  6. package/dist/commands/config.d.ts.map +1 -0
  7. package/dist/commands/config.js +128 -0
  8. package/dist/commands/config.js.map +1 -0
  9. package/dist/commands/create.d.ts +7 -0
  10. package/dist/commands/create.d.ts.map +1 -0
  11. package/dist/commands/create.js +449 -0
  12. package/dist/commands/create.js.map +1 -0
  13. package/dist/commands/feedback.d.ts +6 -0
  14. package/dist/commands/feedback.d.ts.map +1 -0
  15. package/dist/commands/feedback.js +137 -0
  16. package/dist/commands/feedback.js.map +1 -0
  17. package/dist/commands/get.d.ts +6 -0
  18. package/dist/commands/get.d.ts.map +1 -0
  19. package/dist/commands/get.js +122 -0
  20. package/dist/commands/get.js.map +1 -0
  21. package/dist/commands/index.d.ts +13 -0
  22. package/dist/commands/index.d.ts.map +1 -0
  23. package/dist/commands/index.js +13 -0
  24. package/dist/commands/index.js.map +1 -0
  25. package/dist/commands/publish.d.ts +7 -0
  26. package/dist/commands/publish.d.ts.map +1 -0
  27. package/dist/commands/publish.js +593 -0
  28. package/dist/commands/publish.js.map +1 -0
  29. package/dist/commands/scan.d.ts +6 -0
  30. package/dist/commands/scan.d.ts.map +1 -0
  31. package/dist/commands/scan.js +165 -0
  32. package/dist/commands/scan.js.map +1 -0
  33. package/dist/commands/search.d.ts +6 -0
  34. package/dist/commands/search.d.ts.map +1 -0
  35. package/dist/commands/search.js +80 -0
  36. package/dist/commands/search.js.map +1 -0
  37. package/dist/commands/validate.d.ts +7 -0
  38. package/dist/commands/validate.d.ts.map +1 -0
  39. package/dist/commands/validate.js +328 -0
  40. package/dist/commands/validate.js.map +1 -0
  41. package/dist/index.d.ts +6 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +107 -0
  44. package/dist/index.js.map +1 -0
  45. package/package.json +51 -0
  46. package/src/commands/cache.ts +166 -0
  47. package/src/commands/config.ts +142 -0
  48. package/src/commands/create.ts +490 -0
  49. package/src/commands/feedback.ts +161 -0
  50. package/src/commands/get.ts +141 -0
  51. package/src/commands/index.ts +13 -0
  52. package/src/commands/publish.ts +688 -0
  53. package/src/commands/scan.ts +190 -0
  54. package/src/commands/search.ts +92 -0
  55. package/src/commands/validate.ts +391 -0
  56. package/src/index.ts +118 -0
  57. package/tsconfig.json +13 -0
package/dist/index.js ADDED
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Open Skills Hub CLI - Entry Point
4
+ */
5
+ import { Command } from 'commander';
6
+ import chalk from 'chalk';
7
+ import { initConfig } from '@open-skills-hub/core';
8
+ import { searchCommand } from './commands/search.js';
9
+ import { getCommand } from './commands/get.js';
10
+ import { publishCommand } from './commands/publish.js';
11
+ import { validateCommand } from './commands/validate.js';
12
+ import { createCommand } from './commands/create.js';
13
+ import { scanCommand } from './commands/scan.js';
14
+ import { feedbackCommand } from './commands/feedback.js';
15
+ import { cacheCommand } from './commands/cache.js';
16
+ import { configCommand } from './commands/config.js';
17
+ // Package info
18
+ const VERSION = '1.0.0';
19
+ const DESCRIPTION = 'Open Skills Hub CLI - AI 时代的 npm + Docker Hub';
20
+ // Create the CLI program
21
+ const program = new Command();
22
+ program
23
+ .name('skills')
24
+ .version(VERSION)
25
+ .description(DESCRIPTION)
26
+ .option('-v, --verbose', 'Enable verbose output')
27
+ .option('-q, --quiet', 'Suppress output')
28
+ .option('--config <path>', 'Path to configuration file')
29
+ .hook('preAction', async (thisCommand) => {
30
+ // Initialize configuration
31
+ const options = thisCommand.opts();
32
+ const config = initConfig(options['config']);
33
+ await config.load();
34
+ // Set log level based on flags
35
+ if (options['verbose']) {
36
+ config.setValue('logLevel', 'debug');
37
+ }
38
+ else if (options['quiet']) {
39
+ config.setValue('logLevel', 'error');
40
+ }
41
+ });
42
+ // Register commands
43
+ program.addCommand(searchCommand);
44
+ program.addCommand(getCommand);
45
+ program.addCommand(createCommand);
46
+ program.addCommand(validateCommand);
47
+ program.addCommand(publishCommand);
48
+ program.addCommand(scanCommand);
49
+ program.addCommand(feedbackCommand);
50
+ program.addCommand(cacheCommand);
51
+ program.addCommand(configCommand);
52
+ // Help text customization
53
+ program.configureHelp({
54
+ sortSubcommands: true,
55
+ subcommandTerm: (cmd) => cmd.name(),
56
+ });
57
+ // Custom help
58
+ program.addHelpText('after', `
59
+ ${chalk.bold('Examples:')}
60
+ ${chalk.gray('# Create a new skill')}
61
+ $ skills create my-skill
62
+
63
+ ${chalk.gray('# Validate a skill')}
64
+ $ skills validate ./my-skill
65
+
66
+ ${chalk.gray('# Publish a skill directory')}
67
+ $ skills publish ./my-skill
68
+
69
+ ${chalk.gray('# Search for skills')}
70
+ $ skills search "code review"
71
+
72
+ ${chalk.gray('# Get skill content')}
73
+ $ skills get code-reviewer
74
+
75
+ ${chalk.gray('# Scan content for security issues')}
76
+ $ skills scan ./my-skill.md
77
+
78
+ ${chalk.bold('Documentation:')}
79
+ https://github.com/OpenSkillsHub/open-skills-hub
80
+ `);
81
+ // Error handling
82
+ program.exitOverride();
83
+ // Run the CLI
84
+ async function main() {
85
+ try {
86
+ await program.parseAsync(process.argv);
87
+ // Explicitly exit after successful command execution
88
+ // This ensures the process doesn't hang waiting for async operations
89
+ process.exit(0);
90
+ }
91
+ catch (error) {
92
+ if (error instanceof Error) {
93
+ // Commander's exit override throws errors for help/version
94
+ if (error.message.includes('(commander.helpDisplayed)') ||
95
+ error.message.includes('(commander.version)')) {
96
+ process.exit(0);
97
+ }
98
+ console.error(chalk.red('\nError:'), error.message);
99
+ if (process.env['DEBUG']) {
100
+ console.error(chalk.gray(error.stack));
101
+ }
102
+ }
103
+ process.exit(1);
104
+ }
105
+ }
106
+ main();
107
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@open-skills-hub/cli",
3
+ "version": "1.0.0",
4
+ "description": "CLI for Open Skills Hub",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "bin": {
9
+ "skills": "./dist/index.js"
10
+ },
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "dev": "tsx watch src/index.ts",
14
+ "start": "node dist/index.js",
15
+ "clean": "rm -rf dist"
16
+ },
17
+ "dependencies": {
18
+ "@open-skills-hub/core": "1.0.0",
19
+ "commander": "^11.1.0",
20
+ "chalk": "^5.3.0",
21
+ "ora": "^8.0.1",
22
+ "inquirer": "^9.2.12",
23
+ "cli-table3": "^0.6.3",
24
+ "yaml": "^2.3.4",
25
+ "zod": "^3.22.4"
26
+ },
27
+ "devDependencies": {
28
+ "tsx": "^4.7.0",
29
+ "@types/node": "^20.10.6",
30
+ "@types/inquirer": "^9.0.7"
31
+ },
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "https://github.com/OpenSkillsHub/open-skills-hub.git",
35
+ "directory": "packages/cli"
36
+ },
37
+ "bugs": {
38
+ "url": "https://github.com/OpenSkillsHub/open-skills-hub/issues"
39
+ },
40
+ "homepage": "https://github.com/OpenSkillsHub/open-skills-hub#readme",
41
+ "keywords": [
42
+ "skills",
43
+ "ai",
44
+ "agent",
45
+ "cli",
46
+ "command-line",
47
+ "mcp"
48
+ ],
49
+ "author": "Open Skills Hub Team",
50
+ "license": "MIT"
51
+ }
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Open Skills Hub CLI - Cache Command
3
+ */
4
+
5
+ import { Command } from 'commander';
6
+ import chalk from 'chalk';
7
+ import ora from 'ora';
8
+ import Table from 'cli-table3';
9
+ import { getStorage, getConfig, formatBytes } from '@open-skills-hub/core';
10
+
11
+ export const cacheCommand = new Command('cache')
12
+ .description('Manage local cache')
13
+ .addCommand(
14
+ new Command('list')
15
+ .description('List cached skills')
16
+ .option('--json', 'Output as JSON')
17
+ .action(async (options) => {
18
+ const spinner = ora('Loading cache info...').start();
19
+
20
+ try {
21
+ const storage = await getStorage();
22
+ await storage.initialize();
23
+
24
+ const cacheEntries = await storage.getAllCacheMetadata();
25
+ spinner.stop();
26
+
27
+ if (options.json) {
28
+ console.log(JSON.stringify(cacheEntries, null, 2));
29
+ return;
30
+ }
31
+
32
+ if (cacheEntries.length === 0) {
33
+ console.log(chalk.yellow('\nNo cached skills found.'));
34
+ return;
35
+ }
36
+
37
+ console.log(chalk.bold(`\nCached Skills (${cacheEntries.length}):\n`));
38
+
39
+ const table = new Table({
40
+ head: [
41
+ chalk.cyan('Skill'),
42
+ chalk.cyan('Version'),
43
+ chalk.cyan('Size'),
44
+ chalk.cyan('Hits'),
45
+ chalk.cyan('Cached At'),
46
+ ],
47
+ colWidths: [30, 12, 12, 8, 25],
48
+ });
49
+
50
+ let totalSize = 0;
51
+ let totalHits = 0;
52
+
53
+ for (const entry of cacheEntries) {
54
+ table.push([
55
+ entry.skillName,
56
+ entry.version,
57
+ formatBytes(entry.size ?? 0),
58
+ entry.hitCount.toString(),
59
+ entry.cachedAt.split('T')[0] ?? '',
60
+ ]);
61
+ totalSize += entry.size ?? 0;
62
+ totalHits += entry.hitCount;
63
+ }
64
+
65
+ console.log(table.toString());
66
+ console.log();
67
+ console.log(chalk.gray(`Total size: ${formatBytes(totalSize)}`));
68
+ console.log(chalk.gray(`Total hits: ${totalHits}`));
69
+
70
+ await storage.close();
71
+ } catch (error) {
72
+ spinner.fail('Failed to list cache');
73
+ throw error;
74
+ }
75
+ })
76
+ )
77
+ .addCommand(
78
+ new Command('clear')
79
+ .description('Clear cached skills')
80
+ .option('-n, --name <name>', 'Clear specific skill')
81
+ .option('-a, --all', 'Clear all cache')
82
+ .option('-e, --expired', 'Clear only expired entries')
83
+ .action(async (options) => {
84
+ const spinner = ora('Clearing cache...').start();
85
+
86
+ try {
87
+ const storage = await getStorage();
88
+ await storage.initialize();
89
+
90
+ let cleared = 0;
91
+
92
+ if (options.expired) {
93
+ const expiredEntries = await storage.getExpiredCacheEntries();
94
+ for (const entry of expiredEntries) {
95
+ await storage.deleteCacheMetadata(entry.skillName, entry.version);
96
+ cleared++;
97
+ }
98
+ } else if (options.name) {
99
+ const deleted = await storage.deleteCacheMetadata(options.name);
100
+ if (deleted) cleared++;
101
+ } else if (options.all) {
102
+ const allEntries = await storage.getAllCacheMetadata();
103
+ for (const entry of allEntries) {
104
+ await storage.deleteCacheMetadata(entry.skillName, entry.version);
105
+ cleared++;
106
+ }
107
+ } else {
108
+ spinner.info('Specify --all, --expired, or --name <skill>');
109
+ return;
110
+ }
111
+
112
+ spinner.succeed(`Cleared ${cleared} cache entries`);
113
+ await storage.close();
114
+ } catch (error) {
115
+ spinner.fail('Failed to clear cache');
116
+ throw error;
117
+ }
118
+ })
119
+ )
120
+ .addCommand(
121
+ new Command('stats')
122
+ .description('Show cache statistics')
123
+ .action(async () => {
124
+ const spinner = ora('Calculating statistics...').start();
125
+
126
+ try {
127
+ const storage = await getStorage();
128
+ await storage.initialize();
129
+ const config = getConfig();
130
+
131
+ const cacheEntries = await storage.getAllCacheMetadata();
132
+ const expiredEntries = await storage.getExpiredCacheEntries();
133
+
134
+ spinner.stop();
135
+
136
+ const totalSize = cacheEntries.reduce((sum, c) => sum + (c.size ?? 0), 0);
137
+ const totalHits = cacheEntries.reduce((sum, c) => sum + c.hitCount, 0);
138
+ const uniqueSkills = new Set(cacheEntries.map(c => c.skillName)).size;
139
+
140
+ console.log(chalk.bold('\nCache Statistics:\n'));
141
+ console.log(` ${chalk.cyan('Total entries:')} ${cacheEntries.length}`);
142
+ console.log(` ${chalk.cyan('Unique skills:')} ${uniqueSkills}`);
143
+ console.log(` ${chalk.cyan('Total size:')} ${formatBytes(totalSize)}`);
144
+ console.log(` ${chalk.cyan('Max size:')} ${formatBytes(config.get().cache.maxSize)}`);
145
+ console.log(` ${chalk.cyan('Usage:')} ${Math.round((totalSize / config.get().cache.maxSize) * 100)}%`);
146
+ console.log(` ${chalk.cyan('Total hits:')} ${totalHits}`);
147
+ console.log(` ${chalk.cyan('Expired:')} ${expiredEntries.length}`);
148
+ console.log(` ${chalk.cyan('TTL:')} ${config.get().cache.ttl}s`);
149
+ console.log();
150
+
151
+ // Most accessed
152
+ if (cacheEntries.length > 0) {
153
+ const sorted = [...cacheEntries].sort((a, b) => b.hitCount - a.hitCount);
154
+ console.log(chalk.bold('Most accessed:'));
155
+ for (const entry of sorted.slice(0, 5)) {
156
+ console.log(` • ${entry.skillName}@${entry.version} (${entry.hitCount} hits)`);
157
+ }
158
+ }
159
+
160
+ await storage.close();
161
+ } catch (error) {
162
+ spinner.fail('Failed to get statistics');
163
+ throw error;
164
+ }
165
+ })
166
+ );
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Open Skills Hub CLI - Config Command
3
+ */
4
+
5
+ import { Command } from 'commander';
6
+ import chalk from 'chalk';
7
+ import ora from 'ora';
8
+ import * as fs from 'fs';
9
+ import { getConfig, initConfig } from '@open-skills-hub/core';
10
+ import { stringify as stringifyYaml } from 'yaml';
11
+
12
+ export const configCommand = new Command('config')
13
+ .description('Manage configuration')
14
+ .addCommand(
15
+ new Command('show')
16
+ .description('Show current configuration')
17
+ .option('--json', 'Output as JSON')
18
+ .action(async (options) => {
19
+ const config = getConfig();
20
+ const current = config.get();
21
+
22
+ if (options.json) {
23
+ console.log(JSON.stringify(current, null, 2));
24
+ return;
25
+ }
26
+
27
+ console.log(chalk.bold('\nCurrent Configuration:\n'));
28
+ console.log(stringifyYaml(current, { indent: 2 }));
29
+ })
30
+ )
31
+ .addCommand(
32
+ new Command('get')
33
+ .description('Get a specific configuration value')
34
+ .argument('<key>', 'Configuration key (e.g., mode, server.port)')
35
+ .action(async (key) => {
36
+ const config = getConfig();
37
+ const current = config.get();
38
+
39
+ // Navigate nested keys
40
+ const keys = key.split('.');
41
+ let value: unknown = current;
42
+ for (const k of keys) {
43
+ if (value && typeof value === 'object' && k in value) {
44
+ value = (value as Record<string, unknown>)[k];
45
+ } else {
46
+ console.log(chalk.red(`Key not found: ${key}`));
47
+ process.exit(1);
48
+ }
49
+ }
50
+
51
+ if (typeof value === 'object') {
52
+ console.log(JSON.stringify(value, null, 2));
53
+ } else {
54
+ console.log(value);
55
+ }
56
+ })
57
+ )
58
+ .addCommand(
59
+ new Command('set')
60
+ .description('Set a configuration value')
61
+ .argument('<key>', 'Configuration key')
62
+ .argument('<value>', 'Value to set')
63
+ .action(async (key, value) => {
64
+ const spinner = ora('Saving configuration...').start();
65
+
66
+ try {
67
+ const config = getConfig();
68
+ const current = config.get();
69
+
70
+ // Parse value
71
+ let parsedValue: unknown = value;
72
+ if (value === 'true') parsedValue = true;
73
+ else if (value === 'false') parsedValue = false;
74
+ else if (/^\d+$/.test(value)) parsedValue = parseInt(value, 10);
75
+ else if (/^\d+\.\d+$/.test(value)) parsedValue = parseFloat(value);
76
+
77
+ // Navigate and set nested keys
78
+ const keys = key.split('.');
79
+ if (keys.length === 1) {
80
+ config.setValue(key as keyof typeof current, parsedValue as typeof current[keyof typeof current]);
81
+ } else {
82
+ // Handle nested keys
83
+ let target: Record<string, unknown> = current;
84
+ for (let i = 0; i < keys.length - 1; i++) {
85
+ const k = keys[i]!;
86
+ if (!(k in target)) {
87
+ target[k] = {};
88
+ }
89
+ target = target[k] as Record<string, unknown>;
90
+ }
91
+ target[keys[keys.length - 1]!] = parsedValue;
92
+ config.set(current);
93
+ }
94
+
95
+ await config.save();
96
+ spinner.succeed(`Set ${key} = ${parsedValue}`);
97
+ } catch (error) {
98
+ spinner.fail('Failed to save configuration');
99
+ throw error;
100
+ }
101
+ })
102
+ )
103
+ .addCommand(
104
+ new Command('reset')
105
+ .description('Reset configuration to defaults')
106
+ .option('-y, --yes', 'Skip confirmation')
107
+ .action(async (options) => {
108
+ if (!options.yes) {
109
+ console.log(chalk.yellow('This will reset all configuration to defaults.'));
110
+ console.log('Use --yes to confirm.');
111
+ return;
112
+ }
113
+
114
+ const spinner = ora('Resetting configuration...').start();
115
+
116
+ try {
117
+ const config = getConfig();
118
+ config.reset();
119
+ await config.save();
120
+ spinner.succeed('Configuration reset to defaults');
121
+ } catch (error) {
122
+ spinner.fail('Failed to reset configuration');
123
+ throw error;
124
+ }
125
+ })
126
+ )
127
+ .addCommand(
128
+ new Command('path')
129
+ .description('Show configuration file paths')
130
+ .action(async () => {
131
+ const config = getConfig();
132
+
133
+ console.log(chalk.bold('\nConfiguration Paths:\n'));
134
+ console.log(` ${chalk.cyan('Storage:')} ${config.getStoragePath()}`);
135
+ console.log(` ${chalk.cyan('Database:')} ${config.getDatabasePath()}`);
136
+ console.log(` ${chalk.cyan('Cache:')} ${config.getCachePath()}`);
137
+ console.log(` ${chalk.cyan('Local:')} ${config.getLocalSkillsPath()}`);
138
+ console.log(` ${chalk.cyan('Logs:')} ${config.getLogsPath()}`);
139
+ console.log(` ${chalk.cyan('Queue:')} ${config.getQueuePath()}`);
140
+ console.log();
141
+ })
142
+ );