@renwin/ccc 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/LICENSE +21 -0
- package/README.md +192 -0
- package/bin/cli.js +43 -0
- package/lib/config.js +28 -0
- package/lib/install.js +99 -0
- package/lib/list.js +58 -0
- package/package.json +32 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# CCC CLI - ClawHub Clone 命令行工具
|
|
2
|
+
|
|
3
|
+
一个用于安装 AI Agent Skills 的命令行工具,类似于 `npx clawhub@latest install`。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 功能特性
|
|
8
|
+
|
|
9
|
+
- 📦 **安装 Skills**: 从 ClawHub registry 下载并安装 skills
|
|
10
|
+
- 📋 **列表查看**: 浏览所有可用的 skills
|
|
11
|
+
- 🔍 **搜索功能**: 按名称或描述搜索 skills
|
|
12
|
+
- 🎨 **美观输出**: 带颜色和进度提示的终端界面
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 安装
|
|
17
|
+
|
|
18
|
+
### 方式一:全局安装(推荐)
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install -g ccc
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### 方式二:使用 npx(无需安装)
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npx ccc@latest install owner/skill-name
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### 方式三:本地开发
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
git clone <repo-url>
|
|
34
|
+
cd ccc-cli
|
|
35
|
+
npm install
|
|
36
|
+
npm link
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 使用方法
|
|
42
|
+
|
|
43
|
+
### 安装 Skill
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# 基本用法
|
|
47
|
+
ccc install owner/skill-name
|
|
48
|
+
|
|
49
|
+
# 指定安装目录
|
|
50
|
+
ccc install owner/skill-name --dir my-skills
|
|
51
|
+
|
|
52
|
+
# 示例
|
|
53
|
+
ccc install steipete/trello
|
|
54
|
+
ccc install daaab/ethermail
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
安装后的文件位置:`.skills/skill-name/`
|
|
58
|
+
|
|
59
|
+
### 查看所有 Skills
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# 列出所有 skills
|
|
63
|
+
ccc list
|
|
64
|
+
|
|
65
|
+
# 搜索 skills
|
|
66
|
+
ccc list --search trello
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 查看帮助
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
ccc --help
|
|
73
|
+
ccc install --help
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 命令详解
|
|
79
|
+
|
|
80
|
+
### `ccc install <skill>`
|
|
81
|
+
|
|
82
|
+
安装指定的 skill。
|
|
83
|
+
|
|
84
|
+
**参数**:
|
|
85
|
+
- `<skill>` - Skill 名称,格式:`owner/skill-name`
|
|
86
|
+
|
|
87
|
+
**选项**:
|
|
88
|
+
- `-d, --dir <directory>` - 安装目录(默认:`.skills`)
|
|
89
|
+
|
|
90
|
+
**示例**:
|
|
91
|
+
```bash
|
|
92
|
+
ccc install steipete/trello
|
|
93
|
+
ccc install daaab/ethermail --dir my-agents-skills
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### `ccc list`
|
|
97
|
+
|
|
98
|
+
列出所有可用的 skills。
|
|
99
|
+
|
|
100
|
+
**选项**:
|
|
101
|
+
- `-s, --search <query>` - 搜索关键词
|
|
102
|
+
|
|
103
|
+
**示例**:
|
|
104
|
+
```bash
|
|
105
|
+
ccc list
|
|
106
|
+
ccc list --search calendar
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## 工作原理
|
|
112
|
+
|
|
113
|
+
1. **连接 API**: CLI 工具连接到 ClawHub Clone API
|
|
114
|
+
2. **验证 Skill**: 检查 skill 是否存在
|
|
115
|
+
3. **下载 ZIP**: 从服务器下载 skill 的 ZIP 包
|
|
116
|
+
4. **解压文件**: 解压到目标目录
|
|
117
|
+
5. **完成提示**: 显示安装位置和下一步操作
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## 依赖
|
|
122
|
+
|
|
123
|
+
- **commander** - CLI 框架
|
|
124
|
+
- **chalk** - 终端颜色
|
|
125
|
+
- **ora** - 加载动画
|
|
126
|
+
- **adm-zip** - ZIP 解压
|
|
127
|
+
- **node-fetch** - HTTP 请求
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## 开发
|
|
132
|
+
|
|
133
|
+
### 项目结构
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
ccc-cli/
|
|
137
|
+
├── bin/
|
|
138
|
+
│ └── cli.js # CLI 入口
|
|
139
|
+
├── lib/
|
|
140
|
+
│ ├── install.js # 安装逻辑
|
|
141
|
+
│ └── list.js # 列表逻辑
|
|
142
|
+
├── package.json
|
|
143
|
+
└── README.md
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### 本地测试
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
# 安装依赖
|
|
150
|
+
npm install
|
|
151
|
+
|
|
152
|
+
# 链接到全局
|
|
153
|
+
npm link
|
|
154
|
+
|
|
155
|
+
# 测试命令
|
|
156
|
+
ccc install test/skill
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### 发布到 npm
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
# 登录 npm
|
|
163
|
+
npm login
|
|
164
|
+
|
|
165
|
+
# 发布包
|
|
166
|
+
npm publish
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## 配置
|
|
172
|
+
|
|
173
|
+
CLI 工具默认连接到本地 API (`http://localhost:3001`)。
|
|
174
|
+
|
|
175
|
+
要修改 API 地址,编辑 `lib/install.js` 和 `lib/list.js` 中的 `API_BASE` 常量:
|
|
176
|
+
|
|
177
|
+
```javascript
|
|
178
|
+
const API_BASE = 'https://your-production-api.com/api';
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## 许可证
|
|
184
|
+
|
|
185
|
+
MIT
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## 相关项目
|
|
190
|
+
|
|
191
|
+
- [ClawHub Clone](../README.md) - 主项目
|
|
192
|
+
- [ClawHub.ai](https://clawhub.ai/) - 原始项目
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { installSkill } from '../lib/install.js';
|
|
6
|
+
import { listSkills } from '../lib/list.js';
|
|
7
|
+
|
|
8
|
+
const program = new Command();
|
|
9
|
+
|
|
10
|
+
program
|
|
11
|
+
.name('ccc')
|
|
12
|
+
.description('ClawHub Clone CLI - Install AI agent skills')
|
|
13
|
+
.version('1.0.0');
|
|
14
|
+
|
|
15
|
+
// Install command
|
|
16
|
+
program
|
|
17
|
+
.command('install <skill>')
|
|
18
|
+
.description('Install a skill from ClawHub registry')
|
|
19
|
+
.option('-d, --dir <directory>', 'Installation directory', '.skills')
|
|
20
|
+
.action(async (skill, options) => {
|
|
21
|
+
try {
|
|
22
|
+
await installSkill(skill, options.dir);
|
|
23
|
+
} catch (error) {
|
|
24
|
+
console.error(chalk.red('✗ Error:'), error.message);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// List command
|
|
30
|
+
program
|
|
31
|
+
.command('list')
|
|
32
|
+
.description('List all available skills')
|
|
33
|
+
.option('-s, --search <query>', 'Search skills')
|
|
34
|
+
.action(async (options) => {
|
|
35
|
+
try {
|
|
36
|
+
await listSkills(options.search);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
console.error(chalk.red('✗ Error:'), error.message);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
program.parse(process.argv);
|
package/lib/config.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration file for CCC CLI
|
|
3
|
+
* Centralized API and environment settings
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export const config = {
|
|
7
|
+
// API Base URL
|
|
8
|
+
// Production
|
|
9
|
+
apiBase: process.env.CCC_API_BASE || 'https://www.ccc.onl/api',
|
|
10
|
+
|
|
11
|
+
// Development (uncomment to use local API)
|
|
12
|
+
// apiBase: 'http://localhost:3001/api',
|
|
13
|
+
|
|
14
|
+
// Installation directory
|
|
15
|
+
defaultDir: '.skills',
|
|
16
|
+
|
|
17
|
+
// Network settings
|
|
18
|
+
timeout: 30000, // 30 seconds
|
|
19
|
+
|
|
20
|
+
// Version
|
|
21
|
+
version: '1.0.1'
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Environment detection
|
|
26
|
+
*/
|
|
27
|
+
export const isDevelopment = process.env.NODE_ENV === 'development';
|
|
28
|
+
export const isProduction = process.env.NODE_ENV === 'production';
|
package/lib/install.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import fetch from 'node-fetch';
|
|
2
|
+
|
|
3
|
+
// Production API
|
|
4
|
+
const API_BASE = 'https://www.ccc.onl/api';
|
|
5
|
+
|
|
6
|
+
// Development API (uncomment for local testing)
|
|
7
|
+
// const API_BASE = 'http://localhost:3001/api';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Install a skill from ClawHub registry
|
|
11
|
+
* @param {string} skillName - Format: owner/slug
|
|
12
|
+
* @param {string} targetDir - Installation directory
|
|
13
|
+
*/
|
|
14
|
+
export async function installSkill(skillName, targetDir = '.skills') {
|
|
15
|
+
// Parse skill name
|
|
16
|
+
const [owner, slug] = skillName.split('/');
|
|
17
|
+
if (!owner || !slug) {
|
|
18
|
+
throw new Error('Invalid skill name. Use format: owner/skill-name');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const spinner = ora(`Installing ${chalk.cyan(skillName)}...`).start();
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
// Step 1: Verify skill exists
|
|
25
|
+
spinner.text = 'Verifying skill...';
|
|
26
|
+
const skillData = await fetchSkillData(owner, slug);
|
|
27
|
+
|
|
28
|
+
if (!skillData) {
|
|
29
|
+
spinner.fail(`Skill ${chalk.cyan(skillName)} not found`);
|
|
30
|
+
throw new Error('Skill not found in registry');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Step 2: Download skill ZIP
|
|
34
|
+
spinner.text = 'Downloading skill files...';
|
|
35
|
+
const zipBuffer = await downloadSkillZip(owner, slug);
|
|
36
|
+
|
|
37
|
+
// Step 3: Extract to target directory
|
|
38
|
+
spinner.text = 'Extracting files...';
|
|
39
|
+
const skillPath = path.join(process.cwd(), targetDir, slug);
|
|
40
|
+
await extractSkill(zipBuffer, skillPath);
|
|
41
|
+
|
|
42
|
+
// Step 4: Success
|
|
43
|
+
spinner.succeed(chalk.green(`✓ Successfully installed ${chalk.cyan(skillName)}`));
|
|
44
|
+
|
|
45
|
+
console.log();
|
|
46
|
+
console.log(chalk.gray(` Location: ${skillPath}`));
|
|
47
|
+
console.log(chalk.gray(` Version: ${skillData.version || 'N/A'}`));
|
|
48
|
+
console.log();
|
|
49
|
+
console.log(chalk.yellow(' Next steps:'));
|
|
50
|
+
console.log(chalk.gray(` cd ${path.join(targetDir, slug)}`));
|
|
51
|
+
console.log(chalk.gray(` cat SKILL.md`));
|
|
52
|
+
|
|
53
|
+
} catch (error) {
|
|
54
|
+
spinner.fail(chalk.red(`Failed to install ${skillName}`));
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Fetch skill metadata from API
|
|
61
|
+
*/
|
|
62
|
+
async function fetchSkillData(owner, slug) {
|
|
63
|
+
const response = await fetch(`${API_BASE}/skills/${owner}/${slug}`);
|
|
64
|
+
|
|
65
|
+
if (!response.ok) {
|
|
66
|
+
if (response.status === 404) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
throw new Error(`API error: ${response.statusText}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const data = await response.json();
|
|
73
|
+
return data.skill;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Download skill ZIP from API
|
|
78
|
+
*/
|
|
79
|
+
async function downloadSkillZip(owner, slug) {
|
|
80
|
+
const response = await fetch(`${API_BASE}/skills/${owner}/${slug}/download`);
|
|
81
|
+
|
|
82
|
+
if (!response.ok) {
|
|
83
|
+
throw new Error(`Download failed: ${response.statusText}`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return Buffer.from(await response.arrayBuffer());
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Extract ZIP to target directory
|
|
91
|
+
*/
|
|
92
|
+
async function extractSkill(zipBuffer, targetPath) {
|
|
93
|
+
// Create target directory
|
|
94
|
+
await fs.mkdir(targetPath, { recursive: true });
|
|
95
|
+
|
|
96
|
+
// Extract ZIP
|
|
97
|
+
const zip = new AdmZip(zipBuffer);
|
|
98
|
+
zip.extractAllTo(targetPath, true);
|
|
99
|
+
}
|
package/lib/list.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import fetch from 'node-fetch';
|
|
3
|
+
|
|
4
|
+
// Production API
|
|
5
|
+
const API_BASE = 'https://www.ccc.onl/api';
|
|
6
|
+
|
|
7
|
+
// Development API (uncomment for local testing)
|
|
8
|
+
// const API_BASE = 'http://localhost:3001/api';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* List all available skills
|
|
12
|
+
* @param {string} searchQuery - Optional search query
|
|
13
|
+
*/
|
|
14
|
+
export async function listSkills(searchQuery) {
|
|
15
|
+
try {
|
|
16
|
+
// Fetch skills from API
|
|
17
|
+
const url = searchQuery
|
|
18
|
+
? `${API_BASE}/skills?search=${encodeURIComponent(searchQuery)}`
|
|
19
|
+
: `${API_BASE}/skills`;
|
|
20
|
+
|
|
21
|
+
const response = await fetch(url);
|
|
22
|
+
|
|
23
|
+
if (!response.ok) {
|
|
24
|
+
throw new Error(`API error: ${response.statusText}`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const data = await response.json();
|
|
28
|
+
const skills = data.skills || [];
|
|
29
|
+
|
|
30
|
+
if (skills.length === 0) {
|
|
31
|
+
console.log(chalk.yellow('No skills found'));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Display header
|
|
36
|
+
console.log();
|
|
37
|
+
console.log(chalk.bold(`Found ${skills.length} skill(s):`));
|
|
38
|
+
console.log();
|
|
39
|
+
|
|
40
|
+
// Display skills
|
|
41
|
+
skills.forEach(skill => {
|
|
42
|
+
const name = chalk.cyan(`${skill.owner}/${skill.slug}`);
|
|
43
|
+
const version = skill.version ? chalk.gray(`v${skill.version}`) : '';
|
|
44
|
+
const desc = chalk.gray(skill.description || 'No description');
|
|
45
|
+
|
|
46
|
+
console.log(` ${name} ${version}`);
|
|
47
|
+
console.log(` ${desc}`);
|
|
48
|
+
console.log();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Display install hint
|
|
52
|
+
console.log(chalk.gray('To install: ccc install owner/skill-name'));
|
|
53
|
+
console.log();
|
|
54
|
+
|
|
55
|
+
} catch (error) {
|
|
56
|
+
throw new Error(`Failed to list skills: ${error.message}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@renwin/ccc",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "CLI tool for installing ClawHub skills",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"ccc": "./bin/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"clawhub",
|
|
15
|
+
"ai",
|
|
16
|
+
"skills",
|
|
17
|
+
"cli",
|
|
18
|
+
"agent"
|
|
19
|
+
],
|
|
20
|
+
"author": "",
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"commander": "^11.1.0",
|
|
24
|
+
"node-fetch": "^3.3.2",
|
|
25
|
+
"adm-zip": "^0.5.10",
|
|
26
|
+
"chalk": "^5.3.0",
|
|
27
|
+
"ora": "^8.0.1"
|
|
28
|
+
},
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=18.0.0"
|
|
31
|
+
}
|
|
32
|
+
}
|