sk-get 1.0.1

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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [1.0.1] - 2026-01-31
11
+ ### Added
12
+ - 初始化项目结构,实现基础架构和核心功能。
package/README.md ADDED
@@ -0,0 +1,96 @@
1
+ # sk-get
2
+
3
+ 一个简单高效的 CLI 工具,用于从指定的 Git 仓库安装 AI Agent Skills 到 Cursor、Claude 和 VSCode 中。
4
+
5
+ ## 功能
6
+
7
+ - **多平台支持**: 一键安装 Skill 到 Cursor (本地/全局)、Claude (本地) 和 VSCode。
8
+ - **Git 同步**: 自动从远程仓库同步最新的 Skills。
9
+ - **本地缓存**: 缓存仓库内容,提高运行效率。
10
+ - **VSCode 集成**: 自动将 Skill 指令提取并追加到 `.github/copilot-instructions.md`。
11
+
12
+ ## 安装
13
+
14
+ 你可以通过 npm 直接运行:
15
+
16
+ ```bash
17
+ npx sk-get <command>
18
+ ```
19
+
20
+ 或者全局安装:
21
+
22
+ ```bash
23
+ npm install -g sk-get
24
+ ```
25
+
26
+ 安装后,你可以使用 `sk-get` 或简写 `sg` 命令。
27
+
28
+ ## 用法
29
+
30
+ ### 设置仓库地址
31
+
32
+ 在使用之前,你需要设置包含 Skills 的 Git 仓库地址:
33
+
34
+ ```bash
35
+ sg config set-repo <repository-url>
36
+ ```
37
+
38
+ 查看当前配置的仓库:
39
+
40
+ ```bash
41
+ sg config get-repo
42
+ ```
43
+
44
+ ### 列出所有 Skill
45
+
46
+ ```bash
47
+ sg ls
48
+ ```
49
+
50
+ ### 安装 Skill
51
+
52
+ ```bash
53
+ sg install <skill-name> <platform> [options]
54
+ ```
55
+
56
+ **支持的平台 (`platform`):**
57
+
58
+ - `cursor`: 安装到 Cursor 的 skills 目录。
59
+ - `claude`: 安装到 Claude 的 skills 目录。
60
+ - `vscode`: 将 Skill 内容追加到 `.github/copilot-instructions.md`。
61
+
62
+ **选项:**
63
+
64
+ - `-g, --global`: 安装到全局目录(仅支持 `cursor` 和 `claude`)。如果不加此参数,默认安装到当前项目目录下。
65
+
66
+ **示例:**
67
+
68
+ ```bash
69
+ # 将 git-commit 技能安装到当前项目的 Cursor 中
70
+ sg install git-commit cursor
71
+
72
+ # 将 git-commit 技能全局安装到 Cursor 中
73
+ sg install git-commit cursor --global
74
+
75
+ # 将 hello-world 技能安装到 VSCode 配置中
76
+ sg install hello-world vscode
77
+ ```
78
+
79
+ ## 仓库结构要求
80
+
81
+ 你的 Skills 仓库应遵循以下结构:
82
+
83
+ ```text
84
+ .
85
+ └── skills/
86
+ ├── skill-name-1/
87
+ │ └── SKILL.md
88
+ └── skill-name-2/
89
+ └── SKILL.md
90
+ ```
91
+
92
+ 每个 Skill 文件夹必须包含一个 `SKILL.md` 文件。
93
+
94
+ ## 许可证
95
+
96
+ MIT
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import '../dist/index.js';
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "sk-get",
3
+ "version": "1.0.1",
4
+ "type": "module",
5
+ "bin": {
6
+ "sk-get": "./bin/skill-get.js",
7
+ "sg": "./bin/skill-get.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "prepublishOnly": "npm run build",
12
+ "start": "node src/index.ts"
13
+ },
14
+ "keywords": [],
15
+ "author": "",
16
+ "license": "ISC",
17
+ "description": "",
18
+ "dependencies": {
19
+ "chalk": "^5.6.2",
20
+ "commander": "^14.0.3",
21
+ "conf": "^15.0.2",
22
+ "fs-extra": "^11.3.3",
23
+ "simple-git": "^3.30.0"
24
+ },
25
+ "devDependencies": {
26
+ "@types/conf": "^2.1.0",
27
+ "@types/fs-extra": "^11.0.4",
28
+ "@types/node": "^25.1.0",
29
+ "ts-node": "^10.9.2",
30
+ "typescript": "^5.9.3"
31
+ }
32
+ }
@@ -0,0 +1,96 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import chalk from 'chalk';
4
+ import { syncRepo, getAvailableSkills } from '../utils/git.js';
5
+ import {
6
+ getLocalCursorSkillsDir,
7
+ getGlobalCursorSkillsDir,
8
+ getLocalClaudeSkillsDir,
9
+ getGlobalClaudeSkillsDir,
10
+ getVscodeInstructionsPath,
11
+ } from '../utils/paths.js';
12
+
13
+ export async function installCommand(
14
+ skillName: string,
15
+ platform: string,
16
+ options: { global: boolean }
17
+ ) {
18
+ try {
19
+ const cacheDir = await syncRepo();
20
+ const availableSkills = await getAvailableSkills(cacheDir);
21
+
22
+ if (!availableSkills.includes(skillName)) {
23
+ console.error(chalk.red(`Error: Skill "${skillName}" not found.`));
24
+ return;
25
+ }
26
+
27
+ const sourceDir = path.join(cacheDir, 'skills', skillName);
28
+ let targetDir = '';
29
+ let isVscode = false;
30
+
31
+ switch (platform.toLowerCase()) {
32
+ case 'cursor':
33
+ targetDir = options.global
34
+ ? getGlobalCursorSkillsDir()
35
+ : getLocalCursorSkillsDir();
36
+ break;
37
+ case 'claude':
38
+ targetDir = options.global
39
+ ? getGlobalClaudeSkillsDir()
40
+ : getLocalClaudeSkillsDir();
41
+ break;
42
+ case 'vscode':
43
+ isVscode = true;
44
+ if (options.global) {
45
+ console.warn(
46
+ chalk.yellow(
47
+ 'Warning: VSCode does not support global instructions via skill-get yet. Installing to local .github/copilot-instructions.md'
48
+ )
49
+ );
50
+ }
51
+ break;
52
+ default:
53
+ console.error(
54
+ chalk.red(
55
+ `Error: Unsupported platform "${platform}". Use cursor, claude, or vscode.`
56
+ )
57
+ );
58
+ return;
59
+ }
60
+
61
+ if (isVscode) {
62
+ const vscodePath = getVscodeInstructionsPath();
63
+ const skillMdPath = path.join(sourceDir, 'SKILL.md');
64
+
65
+ if (!(await fs.pathExists(skillMdPath))) {
66
+ console.error(chalk.red(`Error: SKILL.md not found in skill "${skillName}".`));
67
+ return;
68
+ }
69
+
70
+ const skillContent = await fs.readFile(skillMdPath, 'utf8');
71
+ await fs.ensureDir(path.dirname(vscodePath));
72
+
73
+ let existingContent = '';
74
+ if (await fs.pathExists(vscodePath)) {
75
+ existingContent = await fs.readFile(vscodePath, 'utf8');
76
+ }
77
+
78
+ if (existingContent.includes(skillContent)) {
79
+ console.log(chalk.yellow(`Skill "${skillName}" is already installed in VSCode instructions.`));
80
+ } else {
81
+ const newContent = existingContent
82
+ ? `${existingContent}\n\n--- \n\n# Skill: ${skillName}\n${skillContent}`
83
+ : `# Skill: ${skillName}\n${skillContent}`;
84
+ await fs.writeFile(vscodePath, newContent);
85
+ console.log(chalk.green(`Successfully installed skill "${skillName}" to ${vscodePath}`));
86
+ }
87
+ } else {
88
+ const targetSkillDir = path.join(targetDir, skillName);
89
+ await fs.ensureDir(targetDir);
90
+ await fs.copy(sourceDir, targetSkillDir);
91
+ console.log(chalk.green(`Successfully installed skill "${skillName}" to ${targetSkillDir}`));
92
+ }
93
+ } catch (error: any) {
94
+ console.error(chalk.red(`Error: ${error.message}`));
95
+ }
96
+ }
@@ -0,0 +1,21 @@
1
+ import chalk from 'chalk';
2
+ import { syncRepo, getAvailableSkills } from '../utils/git.js';
3
+
4
+ export async function listCommand() {
5
+ try {
6
+ const cacheDir = await syncRepo();
7
+ const skills = await getAvailableSkills(cacheDir);
8
+
9
+ if (skills.length === 0) {
10
+ console.log(chalk.yellow('No skills found in the repository.'));
11
+ return;
12
+ }
13
+
14
+ console.log(chalk.green('Available skills:'));
15
+ skills.forEach((skill) => {
16
+ console.log(` - ${skill}`);
17
+ });
18
+ } catch (error: any) {
19
+ console.error(chalk.red(`Error: ${error.message}`));
20
+ }
21
+ }
package/src/index.ts ADDED
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import { listCommand } from './commands/list.js';
5
+ import { installCommand } from './commands/install.js';
6
+ import { setRepoUrl, getRepoUrl } from './utils/config.js';
7
+ import chalk from 'chalk';
8
+
9
+ const program = new Command();
10
+
11
+ program
12
+ .name('sk-get')
13
+ .description('CLI tool to manage AI Agent Skills')
14
+ .version('1.0.0');
15
+
16
+ program
17
+ .command('ls')
18
+ .description('List all available skills from the repository')
19
+ .action(listCommand);
20
+
21
+ program
22
+ .command('install')
23
+ .description('Install a skill to a specific platform')
24
+ .argument('<skill-name>', 'Name of the skill to install')
25
+ .argument('<platform>', 'Target platform (cursor, claude, vscode)')
26
+ .option('-g, --global', 'Install to global directory instead of local', false)
27
+ .action((skillName, platform, options) => {
28
+ installCommand(skillName, platform, options);
29
+ });
30
+
31
+ program
32
+ .command('config')
33
+ .description('Manage configuration')
34
+ .command('set-repo')
35
+ .argument('<url>', 'Repository URL')
36
+ .action((url) => {
37
+ setRepoUrl(url);
38
+ console.log(chalk.green(`Repository URL set to: ${url}`));
39
+ });
40
+
41
+ program
42
+ .command('get-repo')
43
+ .description('Show current repository URL')
44
+ .action(() => {
45
+ console.log(chalk.blue(`Current repository URL: ${getRepoUrl()}`));
46
+ });
47
+
48
+ program.parse(process.argv);
@@ -0,0 +1,12 @@
1
+ import Conf from 'conf';
2
+
3
+ const config = new Conf({
4
+ projectName: 'sk-get',
5
+ defaults: {
6
+ repoUrl: 'https://github.com/yxl/skiil.git'
7
+ }
8
+ });
9
+
10
+ export const getRepoUrl = () => config.get('repoUrl') as string;
11
+ export const setRepoUrl = (url: string) => config.set('repoUrl', url);
12
+ export default config;
@@ -0,0 +1,42 @@
1
+ import { simpleGit, SimpleGit } from 'simple-git';
2
+ import fs from 'fs-extra';
3
+ import { getCacheDir } from './paths.js';
4
+ import { getRepoUrl } from './config.js';
5
+ import chalk from 'chalk';
6
+
7
+ export async function syncRepo(): Promise<string> {
8
+ const cacheDir = getCacheDir();
9
+ const repoUrl = getRepoUrl();
10
+
11
+ if (!repoUrl) {
12
+ throw new Error('Repository URL not configured. Use `skill-get config repoUrl <url>` to set it.');
13
+ }
14
+
15
+ if (!(await fs.pathExists(cacheDir))) {
16
+ console.log(chalk.blue(`Cloning repository ${repoUrl}...`));
17
+ await fs.ensureDir(cacheDir);
18
+ const git: SimpleGit = simpleGit();
19
+ await git.clone(repoUrl, cacheDir);
20
+ } else {
21
+ // console.log(chalk.blue('Updating repository...'));
22
+ const git: SimpleGit = simpleGit(cacheDir);
23
+ try {
24
+ await git.pull();
25
+ } catch (error) {
26
+ console.warn(chalk.yellow('Failed to pull updates, using cached version.'));
27
+ }
28
+ }
29
+
30
+ return cacheDir;
31
+ }
32
+
33
+ export async function getAvailableSkills(cacheDir: string): Promise<string[]> {
34
+ const skillsDir = `${cacheDir}/skills`;
35
+ if (!(await fs.pathExists(skillsDir))) {
36
+ return [];
37
+ }
38
+ const entries = await fs.readdir(skillsDir, { withFileTypes: true });
39
+ return entries
40
+ .filter((entry) => entry.isDirectory())
41
+ .map((entry) => entry.name);
42
+ }
@@ -0,0 +1,16 @@
1
+ import path from 'path';
2
+ import os from 'os';
3
+
4
+ export const getHomeDir = () => os.homedir();
5
+
6
+ export const getGlobalCursorSkillsDir = () => path.join(getHomeDir(), '.cursor', 'skills');
7
+
8
+ export const getLocalCursorSkillsDir = (cwd: string = process.cwd()) => path.join(cwd, '.cursor', 'skills');
9
+
10
+ export const getLocalClaudeSkillsDir = (cwd: string = process.cwd()) => path.join(cwd, '.claude', 'skills');
11
+
12
+ export const getGlobalClaudeSkillsDir = () => path.join(getHomeDir(), '.claude', 'skills');
13
+
14
+ export const getVscodeInstructionsPath = (cwd: string = process.cwd()) => path.join(cwd, '.github', 'copilot-instructions.md');
15
+
16
+ export const getCacheDir = () => path.join(getHomeDir(), '.cache', 'sk-get');
package/tsconfig.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Node",
6
+ "rootDir": "./src",
7
+ "outDir": "./dist",
8
+ "esModuleInterop": true,
9
+ "forceConsistentCasingInFileNames": true,
10
+ "strict": true,
11
+ "skipLibCheck": true,
12
+ "resolveJsonModule": true
13
+ },
14
+ "include": ["src/**/*"]
15
+ }