cvte-skills-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.
package/README.md ADDED
@@ -0,0 +1,173 @@
1
+ # CVTE Skills CLI
2
+
3
+ 一个用于安装自定义 AI 编程助手 Skills 的 CLI 工具,支持多种 AI 助手。
4
+
5
+ ## 支持的 AI 助手
6
+
7
+ | 助手 | 配置文件位置 |
8
+ |------|-------------|
9
+ | Claude Code | `~/.claude/settings.json` (全局) / `CLAUDE.md` (项目) |
10
+ | Cursor | `~/.cursor/rules` (全局) / `.cursorrules` (项目) |
11
+ | Windsurf | `~/.windsurf/rules` (全局) / `.windsurfrules` (项目) |
12
+ | GitHub Copilot | `.github/copilot-instructions.md` |
13
+ | Codex CLI | `~/.codex/instructions.md` (全局) / `codex.md` (项目) |
14
+ | Gemini CLI | `~/.gemini/instructions.md` (全局) / `GEMINI.md` (项目) |
15
+
16
+ ## 包含的 Skills
17
+
18
+ | Skill | 描述 |
19
+ |-------|------|
20
+ | `gitlab-commit-message` | Git commit message 模板规范,强制使用标准化的提交信息格式 |
21
+ | `analysis-opensdk-api` | OpenSDK API 调用链分析工具,用于追踪 SDK 调用流程 |
22
+
23
+ ## 安装
24
+
25
+ ### 通过 npm 全局安装
26
+
27
+ ```bash
28
+ npm install -g cvte-skills-cli
29
+ ```
30
+
31
+ ### 从源码安装
32
+
33
+ ```bash
34
+ git clone https://github.com/YOUR_USERNAME/skills.git
35
+ cd skills
36
+ npm install
37
+ npm link
38
+ ```
39
+
40
+ ## 使用方法
41
+
42
+ ### 初始化 Skills
43
+
44
+ ```bash
45
+ # 进入你的项目目录
46
+ cd /path/to/your/project
47
+
48
+ # 为指定 AI 助手安装 skills
49
+ cvte-skills init --ai claude # Claude Code
50
+ cvte-skills init --ai cursor # Cursor
51
+ cvte-skills init --ai windsurf # Windsurf
52
+ cvte-skills init --ai copilot # GitHub Copilot
53
+ cvte-skills init --ai codex # Codex CLI
54
+ cvte-skills init --ai gemini # Gemini CLI
55
+ cvte-skills init --ai all # 所有助手
56
+ ```
57
+
58
+ ### 全局安装
59
+
60
+ ```bash
61
+ # 安装到全局配置(而非项目级)
62
+ cvte-skills init --ai claude --global
63
+ cvte-skills init --ai cursor --global
64
+ ```
65
+
66
+ ### 选择特定 Skills
67
+
68
+ ```bash
69
+ # 只安装指定的 skills
70
+ cvte-skills init --ai claude -s gitlab-commit-message
71
+ cvte-skills init --ai cursor -s gitlab-commit-message analysis-opensdk-api
72
+ ```
73
+
74
+ ### 查看可用 Skills
75
+
76
+ ```bash
77
+ cvte-skills list
78
+ ```
79
+
80
+ ### 移除 Skills
81
+
82
+ ```bash
83
+ cvte-skills remove --ai claude
84
+ ```
85
+
86
+ ## 命令参考
87
+
88
+ ```
89
+ cvte-skills init [options]
90
+ --ai <assistant> 目标 AI 助手 (claude, cursor, windsurf, copilot, codex, gemini, all)
91
+ --global 安装到全局配置
92
+ -s, --skills <names> 指定要安装的 skills
93
+
94
+ cvte-skills list 列出所有可用的 skills
95
+
96
+ cvte-skills remove 移除已安装的 skills
97
+ --ai <assistant> 目标 AI 助手
98
+ ```
99
+
100
+ ## 项目结构
101
+
102
+ ```
103
+ skills/
104
+ ├── package.json
105
+ ├── bin/
106
+ │ └── cli.js # CLI 入口
107
+ ├── lib/
108
+ │ ├── index.js # 核心逻辑
109
+ │ └── installers/ # 各 AI 助手的安装器
110
+ │ ├── claude.js
111
+ │ ├── cursor.js
112
+ │ ├── windsurf.js
113
+ │ ├── copilot.js
114
+ │ ├── codex.js
115
+ │ └── gemini.js
116
+ └── skills/ # Skill 定义
117
+ ├── gitlab-commit-message/
118
+ │ └── SKILL.md
119
+ └── analysis-opensdk-api/
120
+ └── SKILL.md
121
+ ```
122
+
123
+ ## 创建新 Skill
124
+
125
+ 1. 在 `skills/` 目录下创建新文件夹
126
+ 2. 添加 `SKILL.md` 文件,格式如下:
127
+
128
+ ```markdown
129
+ ---
130
+ name: my-skill
131
+ description: "Skill 的简短描述"
132
+ ---
133
+
134
+ # Skill 标题
135
+
136
+ ## 使用说明
137
+ ...
138
+
139
+ ## 示例
140
+ ...
141
+ ```
142
+
143
+ 3. 重新发布或本地 link 后即可使用
144
+
145
+ ## 手动安装(不使用 CLI)
146
+
147
+ 如果不想使用 CLI,也可以手动配置:
148
+
149
+ ### Claude Code
150
+
151
+ ```bash
152
+ # 方式1: 使用 claude 命令
153
+ claude config add skills /path/to/skills/skills/gitlab-commit-message
154
+
155
+ # 方式2: 编辑 ~/.claude/settings.json
156
+ {
157
+ "skills": [
158
+ "/path/to/skills/skills/gitlab-commit-message"
159
+ ]
160
+ }
161
+ ```
162
+
163
+ ### Cursor
164
+
165
+ 将 skill 内容复制到项目根目录的 `.cursorrules` 文件中。
166
+
167
+ ### GitHub Copilot
168
+
169
+ 将 skill 内容复制到 `.github/copilot-instructions.md` 文件中。
170
+
171
+ ## 许可证
172
+
173
+ MIT
package/bin/cli.js ADDED
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import chalk from 'chalk';
5
+ import { initSkills, listSkills, removeSkills } from '../lib/index.js';
6
+
7
+ const program = new Command();
8
+
9
+ program
10
+ .name('cvte-skills')
11
+ .description('Install custom skills for AI coding assistants')
12
+ .version('1.0.0');
13
+
14
+ program
15
+ .command('init')
16
+ .description('Initialize skills for an AI assistant')
17
+ .option('--ai <assistant>', 'Target AI assistant (claude, cursor, windsurf, copilot, codex, all)')
18
+ .option('--global', 'Install to global config instead of project')
19
+ .option('-s, --skills <skills...>', 'Specific skills to install (default: all)')
20
+ .action(async (options) => {
21
+ if (!options.ai) {
22
+ console.log(chalk.red('Error: Please specify an AI assistant with --ai'));
23
+ console.log(chalk.gray('Available: claude, cursor, windsurf, copilot, codex, gemini, all'));
24
+ process.exit(1);
25
+ }
26
+
27
+ try {
28
+ await initSkills(options);
29
+ console.log(chalk.green('✓ Skills installed successfully!'));
30
+ } catch (error) {
31
+ console.error(chalk.red('Error:'), error.message);
32
+ process.exit(1);
33
+ }
34
+ });
35
+
36
+ program
37
+ .command('list')
38
+ .description('List available skills')
39
+ .action(() => {
40
+ listSkills();
41
+ });
42
+
43
+ program
44
+ .command('remove')
45
+ .description('Remove installed skills')
46
+ .option('--ai <assistant>', 'Target AI assistant')
47
+ .action(async (options) => {
48
+ if (!options.ai) {
49
+ console.log(chalk.red('Error: Please specify an AI assistant with --ai'));
50
+ process.exit(1);
51
+ }
52
+
53
+ try {
54
+ await removeSkills(options);
55
+ console.log(chalk.green('✓ Skills removed successfully!'));
56
+ } catch (error) {
57
+ console.error(chalk.red('Error:'), error.message);
58
+ process.exit(1);
59
+ }
60
+ });
61
+
62
+ program.parse();
package/lib/index.js ADDED
@@ -0,0 +1,141 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import chalk from 'chalk';
4
+ import { fileURLToPath } from 'url';
5
+ import { installForClaude } from './installers/claude.js';
6
+ import { installForCursor } from './installers/cursor.js';
7
+ import { installForWindsurf } from './installers/windsurf.js';
8
+ import { installForCopilot } from './installers/copilot.js';
9
+ import { installForCodex } from './installers/codex.js';
10
+ import { installForGemini } from './installers/gemini.js';
11
+
12
+ const __filename = fileURLToPath(import.meta.url);
13
+ const __dirname = path.dirname(__filename);
14
+
15
+ // 获取 skills 目录
16
+ export function getSkillsDir() {
17
+ return path.join(__dirname, '..', 'skills');
18
+ }
19
+
20
+ // 获取所有可用的 skills
21
+ export function getAvailableSkills() {
22
+ const skillsDir = getSkillsDir();
23
+ const skills = [];
24
+
25
+ if (!fs.existsSync(skillsDir)) {
26
+ return skills;
27
+ }
28
+
29
+ const dirs = fs.readdirSync(skillsDir, { withFileTypes: true });
30
+
31
+ for (const dir of dirs) {
32
+ if (dir.isDirectory()) {
33
+ const skillPath = path.join(skillsDir, dir.name, 'SKILL.md');
34
+ if (fs.existsSync(skillPath)) {
35
+ const content = fs.readFileSync(skillPath, 'utf-8');
36
+ const meta = parseSkillMeta(content);
37
+ skills.push({
38
+ name: dir.name,
39
+ path: path.join(skillsDir, dir.name),
40
+ skillFile: skillPath,
41
+ content: content,
42
+ ...meta
43
+ });
44
+ }
45
+ }
46
+ }
47
+
48
+ return skills;
49
+ }
50
+
51
+ // 解析 SKILL.md 的 frontmatter
52
+ function parseSkillMeta(content) {
53
+ const match = content.match(/^---\s*\n([\s\S]*?)\n---/);
54
+ if (!match) return {};
55
+
56
+ const frontmatter = match[1];
57
+ const meta = {};
58
+
59
+ const nameMatch = frontmatter.match(/name:\s*(.+)/);
60
+ if (nameMatch) meta.displayName = nameMatch[1].trim();
61
+
62
+ const descMatch = frontmatter.match(/description:\s*["']?(.+?)["']?\s*$/m);
63
+ if (descMatch) meta.description = descMatch[1].trim();
64
+
65
+ return meta;
66
+ }
67
+
68
+ // 获取 skill 的纯内容(去掉 frontmatter)
69
+ export function getSkillContent(skill) {
70
+ return skill.content.replace(/^---\s*\n[\s\S]*?\n---\s*\n?/, '').trim();
71
+ }
72
+
73
+ // 列出所有 skills
74
+ export function listSkills() {
75
+ const skills = getAvailableSkills();
76
+
77
+ console.log(chalk.bold('\nAvailable Skills:\n'));
78
+
79
+ if (skills.length === 0) {
80
+ console.log(chalk.gray(' No skills found'));
81
+ return;
82
+ }
83
+
84
+ for (const skill of skills) {
85
+ console.log(chalk.cyan(` ${skill.name}`));
86
+ if (skill.description) {
87
+ console.log(chalk.gray(` ${skill.description}`));
88
+ }
89
+ console.log();
90
+ }
91
+ }
92
+
93
+ // 安装器映射
94
+ const installers = {
95
+ claude: installForClaude,
96
+ cursor: installForCursor,
97
+ windsurf: installForWindsurf,
98
+ copilot: installForCopilot,
99
+ codex: installForCodex,
100
+ gemini: installForGemini,
101
+ };
102
+
103
+ // 初始化 skills
104
+ export async function initSkills(options) {
105
+ const { ai, global: isGlobal, skills: selectedSkills } = options;
106
+
107
+ let allSkills = getAvailableSkills();
108
+
109
+ // 过滤指定的 skills
110
+ if (selectedSkills && selectedSkills.length > 0) {
111
+ allSkills = allSkills.filter(s => selectedSkills.includes(s.name));
112
+ }
113
+
114
+ if (allSkills.length === 0) {
115
+ throw new Error('No skills found to install');
116
+ }
117
+
118
+ console.log(chalk.blue(`\nInstalling ${allSkills.length} skill(s)...\n`));
119
+
120
+ const targets = ai === 'all' ? Object.keys(installers) : [ai];
121
+
122
+ for (const target of targets) {
123
+ const installer = installers[target];
124
+ if (!installer) {
125
+ console.log(chalk.yellow(` ⚠ Unknown assistant: ${target}, skipping`));
126
+ continue;
127
+ }
128
+
129
+ console.log(chalk.gray(` → Installing for ${target}...`));
130
+ await installer(allSkills, { isGlobal });
131
+ console.log(chalk.green(` ✓ ${target}`));
132
+ }
133
+ }
134
+
135
+ // 移除 skills
136
+ export async function removeSkills(options) {
137
+ const { ai } = options;
138
+
139
+ // TODO: 实现移除逻辑
140
+ console.log(chalk.yellow('Remove functionality coming soon...'));
141
+ }
@@ -0,0 +1,83 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import os from 'os';
4
+ import { getSkillsDir } from '../index.js';
5
+
6
+ /**
7
+ * Claude Code 安装器
8
+ *
9
+ * Claude Code 支持两种方式:
10
+ * 1. 全局配置: ~/.claude/settings.json 中的 skills 数组(路径引用)
11
+ * 2. 项目级: .claude/settings.json 或直接写入 CLAUDE.md
12
+ */
13
+ export async function installForClaude(skills, options = {}) {
14
+ const { isGlobal = false } = options;
15
+
16
+ if (isGlobal) {
17
+ // 全局安装:添加 skill 路径到 settings.json
18
+ await installGlobal(skills);
19
+ } else {
20
+ // 项目级安装:写入 CLAUDE.md
21
+ await installProject(skills);
22
+ }
23
+ }
24
+
25
+ async function installGlobal(skills) {
26
+ const settingsPath = path.join(os.homedir(), '.claude', 'settings.json');
27
+
28
+ // 确保目录存在
29
+ await fs.ensureDir(path.dirname(settingsPath));
30
+
31
+ let settings = {};
32
+ if (await fs.pathExists(settingsPath)) {
33
+ try {
34
+ settings = await fs.readJson(settingsPath);
35
+ } catch {
36
+ settings = {};
37
+ }
38
+ }
39
+
40
+ // 初始化 skills 数组
41
+ if (!Array.isArray(settings.skills)) {
42
+ settings.skills = [];
43
+ }
44
+
45
+ // 添加 skill 路径
46
+ for (const skill of skills) {
47
+ if (!settings.skills.includes(skill.path)) {
48
+ settings.skills.push(skill.path);
49
+ }
50
+ }
51
+
52
+ await fs.writeJson(settingsPath, settings, { spaces: 2 });
53
+ }
54
+
55
+ async function installProject(skills) {
56
+ const claudeDir = path.join(process.cwd(), '.claude');
57
+ const claudeMdPath = path.join(process.cwd(), 'CLAUDE.md');
58
+
59
+ // 方式1: 写入 CLAUDE.md(推荐)
60
+ let content = '';
61
+
62
+ if (await fs.pathExists(claudeMdPath)) {
63
+ content = await fs.readFile(claudeMdPath, 'utf-8');
64
+ content += '\n\n';
65
+ }
66
+
67
+ // 添加分隔标记
68
+ content += '<!-- cvte-skills-start -->\n';
69
+ content += '# Custom Skills\n\n';
70
+
71
+ for (const skill of skills) {
72
+ const skillContent = await fs.readFile(skill.skillFile, 'utf-8');
73
+ // 去掉 frontmatter
74
+ const cleanContent = skillContent.replace(/^---\s*\n[\s\S]*?\n---\s*\n?/, '').trim();
75
+ content += `## ${skill.displayName || skill.name}\n\n`;
76
+ content += cleanContent;
77
+ content += '\n\n';
78
+ }
79
+
80
+ content += '<!-- cvte-skills-end -->\n';
81
+
82
+ await fs.writeFile(claudeMdPath, content);
83
+ }
@@ -0,0 +1,44 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import os from 'os';
4
+
5
+ /**
6
+ * OpenAI Codex CLI 安装器
7
+ *
8
+ * Codex 使用:
9
+ * - 全局: ~/.codex/instructions.md
10
+ * - 项目级: codex.md 或 AGENTS.md
11
+ */
12
+ export async function installForCodex(skills, options = {}) {
13
+ const { isGlobal = false } = options;
14
+
15
+ const targetPath = isGlobal
16
+ ? path.join(os.homedir(), '.codex', 'instructions.md')
17
+ : path.join(process.cwd(), 'codex.md');
18
+
19
+ await fs.ensureDir(path.dirname(targetPath));
20
+
21
+ let content = '';
22
+
23
+ if (await fs.pathExists(targetPath)) {
24
+ content = await fs.readFile(targetPath, 'utf-8');
25
+ content = content.replace(/<!-- cvte-skills-start -->[\s\S]*?<!-- cvte-skills-end -->\n?/g, '');
26
+ content = content.trim();
27
+ if (content) content += '\n\n';
28
+ }
29
+
30
+ content += '<!-- cvte-skills-start -->\n';
31
+ content += '# Custom Skills\n\n';
32
+
33
+ for (const skill of skills) {
34
+ const skillContent = await fs.readFile(skill.skillFile, 'utf-8');
35
+ const cleanContent = skillContent.replace(/^---\s*\n[\s\S]*?\n---\s*\n?/, '').trim();
36
+ content += `## ${skill.displayName || skill.name}\n\n`;
37
+ content += cleanContent;
38
+ content += '\n\n';
39
+ }
40
+
41
+ content += '<!-- cvte-skills-end -->\n';
42
+
43
+ await fs.writeFile(targetPath, content);
44
+ }
@@ -0,0 +1,38 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+
4
+ /**
5
+ * GitHub Copilot 安装器
6
+ *
7
+ * Copilot 使用 .github/copilot-instructions.md 文件
8
+ */
9
+ export async function installForCopilot(skills, options = {}) {
10
+ const targetDir = path.join(process.cwd(), '.github');
11
+ const targetPath = path.join(targetDir, 'copilot-instructions.md');
12
+
13
+ await fs.ensureDir(targetDir);
14
+
15
+ let content = '';
16
+
17
+ if (await fs.pathExists(targetPath)) {
18
+ content = await fs.readFile(targetPath, 'utf-8');
19
+ content = content.replace(/<!-- cvte-skills-start -->[\s\S]*?<!-- cvte-skills-end -->\n?/g, '');
20
+ content = content.trim();
21
+ if (content) content += '\n\n';
22
+ }
23
+
24
+ content += '<!-- cvte-skills-start -->\n';
25
+ content += '# Custom Skills\n\n';
26
+
27
+ for (const skill of skills) {
28
+ const skillContent = await fs.readFile(skill.skillFile, 'utf-8');
29
+ const cleanContent = skillContent.replace(/^---\s*\n[\s\S]*?\n---\s*\n?/, '').trim();
30
+ content += `## ${skill.displayName || skill.name}\n\n`;
31
+ content += cleanContent;
32
+ content += '\n\n';
33
+ }
34
+
35
+ content += '<!-- cvte-skills-end -->\n';
36
+
37
+ await fs.writeFile(targetPath, content);
38
+ }
@@ -0,0 +1,43 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import os from 'os';
4
+
5
+ /**
6
+ * Cursor 安装器
7
+ *
8
+ * Cursor 使用 .cursorrules 或 .cursor/rules 文件
9
+ */
10
+ export async function installForCursor(skills, options = {}) {
11
+ const { isGlobal = false } = options;
12
+
13
+ const targetPath = isGlobal
14
+ ? path.join(os.homedir(), '.cursor', 'rules')
15
+ : path.join(process.cwd(), '.cursorrules');
16
+
17
+ // 确保目录存在
18
+ await fs.ensureDir(path.dirname(targetPath));
19
+
20
+ let content = '';
21
+
22
+ if (await fs.pathExists(targetPath)) {
23
+ content = await fs.readFile(targetPath, 'utf-8');
24
+ // 移除旧的 skills 内容
25
+ content = content.replace(/<!-- cvte-skills-start -->[\s\S]*?<!-- cvte-skills-end -->\n?/g, '');
26
+ content = content.trim();
27
+ if (content) content += '\n\n';
28
+ }
29
+
30
+ // 添加 skills
31
+ content += '<!-- cvte-skills-start -->\n';
32
+
33
+ for (const skill of skills) {
34
+ const skillContent = await fs.readFile(skill.skillFile, 'utf-8');
35
+ const cleanContent = skillContent.replace(/^---\s*\n[\s\S]*?\n---\s*\n?/, '').trim();
36
+ content += cleanContent;
37
+ content += '\n\n';
38
+ }
39
+
40
+ content += '<!-- cvte-skills-end -->\n';
41
+
42
+ await fs.writeFile(targetPath, content);
43
+ }
@@ -0,0 +1,44 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import os from 'os';
4
+
5
+ /**
6
+ * Google Gemini CLI 安装器
7
+ *
8
+ * Gemini CLI 使用:
9
+ * - 全局: ~/.gemini/instructions.md
10
+ * - 项目级: GEMINI.md
11
+ */
12
+ export async function installForGemini(skills, options = {}) {
13
+ const { isGlobal = false } = options;
14
+
15
+ const targetPath = isGlobal
16
+ ? path.join(os.homedir(), '.gemini', 'instructions.md')
17
+ : path.join(process.cwd(), 'GEMINI.md');
18
+
19
+ await fs.ensureDir(path.dirname(targetPath));
20
+
21
+ let content = '';
22
+
23
+ if (await fs.pathExists(targetPath)) {
24
+ content = await fs.readFile(targetPath, 'utf-8');
25
+ content = content.replace(/<!-- cvte-skills-start -->[\s\S]*?<!-- cvte-skills-end -->\n?/g, '');
26
+ content = content.trim();
27
+ if (content) content += '\n\n';
28
+ }
29
+
30
+ content += '<!-- cvte-skills-start -->\n';
31
+ content += '# Custom Skills\n\n';
32
+
33
+ for (const skill of skills) {
34
+ const skillContent = await fs.readFile(skill.skillFile, 'utf-8');
35
+ const cleanContent = skillContent.replace(/^---\s*\n[\s\S]*?\n---\s*\n?/, '').trim();
36
+ content += `## ${skill.displayName || skill.name}\n\n`;
37
+ content += cleanContent;
38
+ content += '\n\n';
39
+ }
40
+
41
+ content += '<!-- cvte-skills-end -->\n';
42
+
43
+ await fs.writeFile(targetPath, content);
44
+ }
@@ -0,0 +1,40 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import os from 'os';
4
+
5
+ /**
6
+ * Windsurf 安装器
7
+ *
8
+ * Windsurf 使用 .windsurfrules 文件
9
+ */
10
+ export async function installForWindsurf(skills, options = {}) {
11
+ const { isGlobal = false } = options;
12
+
13
+ const targetPath = isGlobal
14
+ ? path.join(os.homedir(), '.windsurf', 'rules')
15
+ : path.join(process.cwd(), '.windsurfrules');
16
+
17
+ await fs.ensureDir(path.dirname(targetPath));
18
+
19
+ let content = '';
20
+
21
+ if (await fs.pathExists(targetPath)) {
22
+ content = await fs.readFile(targetPath, 'utf-8');
23
+ content = content.replace(/<!-- cvte-skills-start -->[\s\S]*?<!-- cvte-skills-end -->\n?/g, '');
24
+ content = content.trim();
25
+ if (content) content += '\n\n';
26
+ }
27
+
28
+ content += '<!-- cvte-skills-start -->\n';
29
+
30
+ for (const skill of skills) {
31
+ const skillContent = await fs.readFile(skill.skillFile, 'utf-8');
32
+ const cleanContent = skillContent.replace(/^---\s*\n[\s\S]*?\n---\s*\n?/, '').trim();
33
+ content += cleanContent;
34
+ content += '\n\n';
35
+ }
36
+
37
+ content += '<!-- cvte-skills-end -->\n';
38
+
39
+ await fs.writeFile(targetPath, content);
40
+ }
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "cvte-skills-cli",
3
+ "version": "1.0.0",
4
+ "description": "CLI tool to install custom skills for AI coding assistants",
5
+ "type": "module",
6
+ "main": "lib/index.js",
7
+ "bin": {
8
+ "cvte-skills": "./bin/cli.js"
9
+ },
10
+ "scripts": {
11
+ "test": "echo \"No tests yet\""
12
+ },
13
+ "keywords": [
14
+ "claude",
15
+ "cursor",
16
+ "copilot",
17
+ "ai",
18
+ "skills",
19
+ "cli"
20
+ ],
21
+ "author": "",
22
+ "license": "MIT",
23
+ "dependencies": {
24
+ "commander": "^12.0.0",
25
+ "chalk": "^5.3.0",
26
+ "fs-extra": "^11.2.0",
27
+ "inquirer": "^9.2.0"
28
+ },
29
+ "engines": {
30
+ "node": ">=18.0.0"
31
+ },
32
+ "files": [
33
+ "bin",
34
+ "lib",
35
+ "skills"
36
+ ]
37
+ }
@@ -0,0 +1,161 @@
1
+ ---
2
+ name: analysis-opensdk-api
3
+ description: Analyze OpenSDK-initiated API call chains across source app → OpenSDK3.0 → UdiServer → UdiServiceCore. Use when tracing SDKSystemHelper/SDK* calls, Cmd* requests, or
4
+ UDI API URIs from app side. Produce a call-chain flow diagram and file/line references.
5
+ ---
6
+
7
+ # OpenSDK API Chain Analysis
8
+
9
+ ## Prerequisites
10
+ - User must provide or have configured project paths:
11
+ - SeewoOTA: Android OTA app(other source apps)
12
+ - OpenSDK3.0: SDK layer
13
+ - UdiServer: OpenSDK service bridge
14
+ - UdiServiceCore: UDI implementation
15
+ - If paths not provided, ask user before proceeding.
16
+
17
+ ## References
18
+ - OpenSDK3.0: /Users/cvte/Documents/android/project/seewo/OpenSDK3.0/OpenSDK3.0
19
+ - UdiServer: /Users/cvte/Documents/android/project/udi/UdiServer
20
+ - UdiServiceCore: /Users/cvte/Documents/android/project/udi/UdiServiceCore
21
+
22
+
23
+ ## Scope
24
+ - Trace forward: App/SeewoOTA → OpenSDK3.0 → UdiServer → UdiServiceCore.
25
+ - Stop at these boundaries and note the final action:
26
+ - **Shell**: command string (e.g., `ffp_tool`, `eUpdate2`)
27
+ - **VMan**: FwUpgradeManager type (e.g., `TYPE_FW_UPGRADE_TOUCHBIN`)
28
+ - **JNI**: McutouchManager method (e.g., `upgradeTouch`)
29
+ - **System Service**: Intent/ComponentName (e.g., TouchPanelService)
30
+ - **TvApi**: ITvApiManager method
31
+ - Do not assume external services exist; only include if chain explicitly reaches them.
32
+
33
+ ## Inputs to collect
34
+ - Entry keyword (API name, Cmd class, or URI), e.g. `upgradeTouch`, `CmdUpgradeTouch`, `/v1/touch/ota/upgrade`.
35
+ - Target app module: SeewoOTA (the usual entry point).
36
+
37
+ ## Workflow
38
+
39
+ ### 0) Check for VMan architecture (Android 14+)
40
+ - Search for VMan-based tasks:
41
+ - `Grep pattern="VmanUpdateTask|BaseVmanUpdateTask" path=<SeewoOTA_path>`
42
+ - If found, note that the chain may use VMan SDK directly for version checking.
43
+
44
+ ### 1) Find app entry points (SeewoOTA)
45
+ - Search for SDK calls:
46
+ - `Grep pattern="SDKSystemHelper\.I\.|OpenSDK\.getInstance" path=<SeewoOTA_path>`
47
+ - If you have a method name, search directly:
48
+ - `Grep pattern="<keyword>" path=<SeewoOTA_path>`
49
+ - Record: caller class/method, dialog/trigger path, and any pre-checks.
50
+
51
+ ### 1.5) Check config gates
52
+ - Search for config flags:
53
+ - `Grep pattern="R\.bool\.|R\.string\." path=<SeewoOTA_path>/src/main`
54
+ - Check `strings_config.xml` for defaults and flavor overrides.
55
+ - Record: config flag name, default value, controlling flavors.
56
+
57
+ ### 2) Map to OpenSDK3.0
58
+ - Locate SDK method in `SDKSystemHelper.java`:
59
+ - `Grep pattern="<method>\(" path=<OpenSDK_path>/src/main/java/com/seewo/sdk/SDKSystemHelper.java`
60
+ - Open the Cmd class and note its fields.
61
+ - Record: `SDKSystemHelper.<method>` → `Cmd*` → `OpenSDK.postCommand/sendCommand`.
62
+
63
+ ### 3) Map to UdiServer
64
+ - Search for Cmd class:
65
+ - `Grep pattern="Cmd<Name>" path=<UdiServer_path>`
66
+ - Identify handler method (`@HandlePostRequest`, `@HandleGetRequest`, `@HandleRequest`).
67
+ - Note the UDI URI in `RequestBuilder`.
68
+ - Record: handler class/method, request URI, event adapters.
69
+
70
+ ### 4) Map to UdiServiceCore
71
+ - Find URI in `ProtocolDefine.kt`:
72
+ - `Grep pattern="/v1/.*" path=<UdiServiceCore_path>/.../ProtocolDefine.kt`
73
+ - Find `@SetterHandler`/`@GetterHandler` in service core.
74
+ - Trace into implementation class and capture branching logic.
75
+ - Record: service method, implementation class, branch rules, final action.
76
+
77
+ ### 4.5) Trace result/status notifications
78
+ - Check for related notify endpoints:
79
+ - `Grep pattern="<base_uri>/(result|status)" path=<UdiServiceCore_path>`
80
+ - Record: result URI, notify class, callback mechanism.
81
+
82
+ ### 5) Summarize and diagram
83
+ - Provide bullet call chain with file paths + line numbers.
84
+ - Provide ASCII flow diagram.
85
+ - Include branch rules if applicable.
86
+ - Note boundary and final action type.
87
+
88
+ ## Output format
89
+
90
+ ### Structure
91
+ 1. Short intro: what chain traced and entry keyword.
92
+ 2. Call-chain bullets (with file:line refs).
93
+ 3. Flow diagram (ASCII).
94
+ 4. Boundary note with final action type.
95
+
96
+ ### Flow diagram templates
97
+
98
+ #### Linear chain
99
+ ┌───────────────────────────────┐
100
+ │ SeewoOTA │
101
+ │ entry → SDKSystemHelper.* │
102
+ └───────────────────────────────┘
103
+
104
+ ┌───────────────────────────────┐
105
+ │ OpenSDK3.0 │
106
+ │ SDKSystemHelper → Cmd* │
107
+ │ OpenSDK.postCommand │
108
+ └───────────────────────────────┘
109
+
110
+ ┌───────────────────────────────┐
111
+ │ UdiServer │
112
+ │ Handler.* → POST /v1/... │
113
+ └───────────────────────────────┘
114
+
115
+ ┌───────────────────────────────┐
116
+ │ UdiServiceCore │
117
+ │ Service.* → Impl.* │
118
+ └───────────────────────────────┘
119
+
120
+ #### Branching at UdiServiceCore
121
+ ┌─────────────────────────────────────────────────────────────┐
122
+ │ UdiServiceCore │
123
+ │ Service.* → Impl.* │
124
+ │ ┌──────────────────────────────────────────────────────┐ │
125
+ │ │ Branch rules: │ │
126
+ │ │ • IR_HX/FCT/CD → Service: TouchPanelService │ │
127
+ │ │ • IR_FF → Shell: ffp_tool -f USB -i $path │ │
128
+ │ │ • CAP_EETI → Shell: eUpdate2 -f $path ... │ │
129
+ │ │ • IR_KTC → JNI: McutouchManager.upgradeTouch │ │
130
+ │ │ • IR_TY_NOAAR → VMan: TYPE_FW_UPGRADE_TOUCHBIN │ │
131
+ │ └──────────────────────────────────────────────────────┘ │
132
+ └─────────────────────────────────────────────────────────────┘
133
+
134
+ #### VMan direct chain (Android 14+)
135
+ ┌───────────────────────────────┐
136
+ │ SeewoOTA │
137
+ │ *VmanUpdateTask │
138
+ │ FwTouchCtrlManager.fwTouchList│
139
+ └───────────────────────────────┘
140
+
141
+ ┌───────────────────────────────┐
142
+ │ VMan SDK │
143
+ │ EntityFwTouch │
144
+ │ otaUpgradeTouchBinPath │
145
+ └───────────────────────────────┘
146
+
147
+ ┌───────────────────────────────┐
148
+ │ SDKSystemHelper.upgradeTouch │
149
+ │ (continues to OpenSDK chain) │
150
+ └───────────────────────────────┘
151
+
152
+ ## Quality checklist
153
+ - [ ] At least one file:line reference per module
154
+ - [ ] Exact UDI URI and Cmd class named
155
+ - [ ] Property/config gates noted (if present)
156
+ - [ ] Result event endpoints named (if used)
157
+ - [ ] Branch rules documented (if applicable)
158
+ - [ ] Boundary type and final action specified
159
+ - [ ] ASCII only in diagrams
160
+
161
+ ---
@@ -0,0 +1,95 @@
1
+ ———
2
+
3
+ name: gitlab-commit-message
4
+ description: "Enforce the git commit message template and checklist. Use when preparing commit messages or committing."
5
+
6
+ ---
7
+
8
+ # Commit Message Template
9
+
10
+ ## Overview
11
+
12
+ Use this skill to format git commit messages. Keep commits small and cohesive, and always follow the required template below.
13
+
14
+ ## Workflow
15
+
16
+ 1. Check recent commit messages for tone and granularity (optional).
17
+ 2. Ensure the change set is cohesive; split commits if multiple intents exist.
18
+ 3. Write the commit message strictly using the template.
19
+
20
+ ## Commit Message Template (Mandatory)
21
+
22
+ [类型: bugfix | feature | improve | sonar | ...] 简练总结性的描述
23
+
24
+ [why] 为什么修改,修复什么场景,优化什么功能
25
+ [how] 怎么做,做了什么,后续维护
26
+ [influence] 受影响功能模块
27
+ [check] 预期 xxx
28
+
29
+ [jira] NA 占位
30
+
31
+ ### Rules
32
+
33
+ - Keep the summary short and specific.
34
+ - Use only these tags: [why], [how], [influence], [check], [jira].
35
+ - Make [check] steps reproducible and verifiable.
36
+
37
+ ## Examples
38
+
39
+ ### Good
40
+
41
+ [bugfix] 修复自定义图片加载问题
42
+
43
+ [why] 下次打开没有加载上次上传的自定义背景
44
+ [how] 统一使用 ThemeResHelper 管理,修复数据源不一致的问题
45
+ [influence] Background
46
+ [check] 在菜单-主题-背景,上传自定义图片,关闭应用再打开,看是否还存在,应用到背景是否正常
47
+
48
+ [jira] NA
49
+
50
+ ### Bad (overly broad)
51
+
52
+ [feature] 完成 module_formula
53
+ module_rego,module_func 全面重构升级,新增数学计算和图形可视化功能
54
+
55
+ [why] 原有公式模块功能单一且存在稳定性问题,需要提升为支持复杂数学计算和可视化的现代化模块,同时修复LaTeX解析、TabLayout切换、图形渲染等关键问题
56
+ [how] 1.完成100% Java→Kotlin迁移
57
+ 2.新增FormulaCalculator数学计算引擎和ExpressionParser表达式解析器
58
+ 3.实现FunctionGraphView函数图形可视化
59
+ 4.重构UI交互:Done按钮替代回车键确认、修复TabLayout切换机制
60
+ 5.优化LaTeX标准化器,修复正则表达式崩溃问
61
+ 6.新增完整测试用例和demo界面 7.完善文档结构和PlantUML类图
62
+ [influence] module_formula, module_demo, 依赖公式功能的所有白板模块
63
+ [check] 验证公式编辑器弹窗是否正常;TabLayout四个分类切换正常;数学表达式计算功能正常(如x^2+2x-1),函数图形绘制显示正常;FormulaShape Canvas集成绘制正常;
64
+
65
+ [jira] SXRD-17429
66
+
67
+ ### Bad (too vague)
68
+
69
+ [bugfix] 修复
70
+
71
+ [why] 崩溃
72
+ [how] 加判断
73
+ [influence] all
74
+ [check] 无崩溃
75
+
76
+ [jira] S15250039-752
77
+
78
+
79
+ ## Output Format
80
+
81
+ Use EOF format commit message,keep the code style.
82
+
83
+ ```
84
+ [bugfix] ..
85
+
86
+ [why] ..
87
+ [how] ..
88
+ [influence] ..
89
+ [check] ..
90
+
91
+ [jira] ..
92
+ ```
93
+
94
+ ---
95
+