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 +12 -0
- package/README.md +96 -0
- package/bin/skill-get.js +2 -0
- package/package.json +32 -0
- package/src/commands/install.ts +96 -0
- package/src/commands/list.ts +21 -0
- package/src/index.ts +48 -0
- package/src/utils/config.ts +12 -0
- package/src/utils/git.ts +42 -0
- package/src/utils/paths.ts +16 -0
- package/tsconfig.json +15 -0
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
|
package/bin/skill-get.js
ADDED
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;
|
package/src/utils/git.ts
ADDED
|
@@ -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
|
+
}
|