@adonis0123/commit 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.
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "commit",
3
+ "version": "1.0.0",
4
+ "package": "@adonis0123/commit",
5
+ "files": {
6
+ "references": "references/"
7
+ },
8
+ "targets": {
9
+ "claude-code": {
10
+ "enabled": true,
11
+ "paths": {
12
+ "global": ".claude/skills",
13
+ "project": ".claude/skills"
14
+ }
15
+ },
16
+ "cursor": {
17
+ "enabled": true,
18
+ "paths": {
19
+ "global": ".cursor/skills",
20
+ "project": ".cursor/skills"
21
+ }
22
+ }
23
+ }
24
+ }
package/SKILL.md ADDED
@@ -0,0 +1,108 @@
1
+ ---
2
+ name: commit
3
+ description: This skill should be used when the user asks to "commit", "generate commit message", "create commit", "提交代码", "生成提交信息", or needs help writing git commit messages following conventional commit format with emoji prefixes.
4
+ version: 1.0.0
5
+ ---
6
+
7
+ # Commit Message Generator
8
+
9
+ 根据暂存的代码变更自动生成符合 Conventional Commits 规范的提交信息,并自动添加对应的 emoji 前缀。
10
+
11
+ ## 使用场景
12
+
13
+ - 用户执行 `/commit` 命令
14
+ - 用户请求生成提交信息
15
+ - 用户需要帮助编写符合规范的 commit message
16
+
17
+ ## 工作流程
18
+
19
+ ### 1. 检查暂存状态
20
+
21
+ 执行 `git status` 查看当前暂存的文件变更。如果没有暂存的文件,提示用户先使用 `git add` 添加文件。
22
+
23
+ ### 2. 分析代码变更
24
+
25
+ 执行 `git diff --cached` 获取暂存的代码差异,分析变更内容:
26
+
27
+ - 识别变更的文件类型和位置
28
+ - 理解变更的目的(新功能、修复、重构等)
29
+ - 确定影响范围(scope)
30
+
31
+ ### 3. 生成提交信息
32
+
33
+ 根据分析结果生成符合规范的提交信息。
34
+
35
+ **消息格式:**
36
+ ```
37
+ type(scope): subject
38
+ ```
39
+
40
+ **允许的类型和对应 emoji:**
41
+
42
+ | 类型 | Emoji | 说明 | 示例 |
43
+ |------|-------|------|------|
44
+ | feat | ✨ | 新功能 | `✨ feat: add user authentication` |
45
+ | fix | 🐛 | Bug 修复 | `🐛 fix: resolve login timeout` |
46
+ | docs | 📝 | 文档变更 | `📝 docs: update API documentation` |
47
+ | style | 🎨 | 代码风格 | `🎨 style: format code with prettier` |
48
+ | refactor | ♻️ | 代码重构 | `♻️ refactor: extract common utils` |
49
+ | perf | ⚡️ | 性能优化 | `⚡️ perf: optimize database queries` |
50
+ | test | ✅ | 测试相关 | `✅ test: add unit tests for auth` |
51
+ | build | 🏗️ | 构建系统 | `🏗️ build: update webpack config` |
52
+ | ci | 👷 | CI 配置 | `👷 ci: add GitHub Actions workflow` |
53
+ | chore | 🔧 | 其他变更 | `🔧 chore: update dependencies` |
54
+
55
+ ### 4. 执行提交
56
+
57
+ 使用 HEREDOC 格式执行 git commit:
58
+
59
+ ```bash
60
+ git commit -m "$(cat <<'EOF'
61
+ ✨ feat(auth): add user login feature
62
+ EOF
63
+ )"
64
+ ```
65
+
66
+ ## 提交信息编写规则
67
+
68
+ ### Header 规则
69
+
70
+ - 格式:`emoji type(scope): subject`
71
+ - Header 最大长度:250 字符
72
+ - type 必须是允许的类型之一
73
+ - scope 可选,表示影响范围
74
+ - subject 使用祈使句,首字母小写,不加句号
75
+
76
+ ### 类型选择指南
77
+
78
+ - **feat**: 添加新功能或新特性
79
+ - **fix**: 修复 bug 或问题
80
+ - **docs**: 仅文档变更(README、注释等)
81
+ - **style**: 不影响代码含义的变更(格式化、空格等)
82
+ - **refactor**: 既不是新功能也不是修复的代码变更
83
+ - **perf**: 提升性能的代码变更
84
+ - **test**: 添加或修改测试
85
+ - **build**: 影响构建系统或外部依赖的变更
86
+ - **ci**: CI 配置文件和脚本的变更
87
+ - **chore**: 其他不修改 src 或 test 文件的变更
88
+
89
+ ### Scope 建议
90
+
91
+ 根据项目结构选择合适的 scope:
92
+
93
+ - 按模块:`auth`、`api`、`ui`、`db`
94
+ - 按功能:`login`、`payment`、`search`
95
+ - 按目录:`components`、`hooks`、`utils`
96
+
97
+ ## 注意事项
98
+
99
+ - 不要提交包含敏感信息的文件(.env、credentials 等)
100
+ - 提交前确保代码通过 lint 和类型检查
101
+ - 一次提交只做一件事,保持提交的原子性
102
+ - 提交信息要准确反映变更内容,关注"为什么"而非"做了什么"
103
+
104
+ ## 参考资源
105
+
106
+ 详细的提交规范和项目配置,参考:
107
+ - **`references/commit-convention.md`** - 完整的提交规范文档
108
+ - **`references/commit-examples.md`** - 提交信息示例
@@ -0,0 +1,315 @@
1
+ #!/usr/bin/env node
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (let key of __getOwnPropNames(from))
11
+ if (!__hasOwnProp.call(to, key) && key !== except)
12
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
+ }
14
+ return to;
15
+ };
16
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
+ // If the importer is in node compatibility mode or this is not an ESM
18
+ // file that has been converted to a CommonJS file using a Babel-
19
+ // compatible transform (i.e. "__esModule" has not been set), then set
20
+ // "default" to the CommonJS "module.exports" for node compatibility.
21
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
+ mod
23
+ ));
24
+
25
+ // shared/src/install-skill.ts
26
+ var import_fs2 = __toESM(require("fs"));
27
+ var import_path2 = __toESM(require("path"));
28
+ var import_os2 = __toESM(require("os"));
29
+ var import_child_process = require("child_process");
30
+
31
+ // shared/src/utils.ts
32
+ var import_fs = __toESM(require("fs"));
33
+ var import_path = __toESM(require("path"));
34
+ var import_os = __toESM(require("os"));
35
+ var CWD = process.env.INIT_CWD || process.cwd();
36
+ var DEFAULT_TARGET = {
37
+ name: "claude-code",
38
+ paths: {
39
+ global: ".claude/skills",
40
+ project: ".claude/skills"
41
+ }
42
+ };
43
+ function getEnabledTargets(config) {
44
+ if (!config.targets) {
45
+ return [DEFAULT_TARGET];
46
+ }
47
+ return Object.entries(config.targets).filter(([_, target]) => target.enabled).map(([name, target]) => ({
48
+ name,
49
+ paths: target.paths
50
+ }));
51
+ }
52
+ function extractSkillName(packageName) {
53
+ if (packageName.startsWith("@")) {
54
+ return packageName.split("/")[1] || packageName;
55
+ }
56
+ return packageName;
57
+ }
58
+ function detectInstallLocation(targetPaths, isGlobal) {
59
+ if (isGlobal) {
60
+ return {
61
+ type: "personal",
62
+ base: import_path.default.join(import_os.default.homedir(), targetPaths.global)
63
+ };
64
+ }
65
+ let projectRoot = CWD;
66
+ while (projectRoot !== import_path.default.dirname(projectRoot)) {
67
+ const hasPackageJson = import_fs.default.existsSync(import_path.default.join(projectRoot, "package.json"));
68
+ const hasGit = import_fs.default.existsSync(import_path.default.join(projectRoot, ".git"));
69
+ const isInNodeModules = projectRoot.includes("/node_modules/") || import_path.default.basename(projectRoot) === "node_modules";
70
+ if ((hasPackageJson || hasGit) && !isInNodeModules) {
71
+ break;
72
+ }
73
+ projectRoot = import_path.default.dirname(projectRoot);
74
+ }
75
+ const finalIsInNodeModules = projectRoot.includes("/node_modules/") || import_path.default.basename(projectRoot) === "node_modules";
76
+ if (finalIsInNodeModules) {
77
+ console.warn("\u26A0 Warning: Could not find project root directory, using current directory");
78
+ projectRoot = CWD;
79
+ }
80
+ return {
81
+ type: "project",
82
+ base: import_path.default.join(projectRoot, targetPaths.project)
83
+ };
84
+ }
85
+ function isGlobalInstall() {
86
+ return process.env.npm_config_global === "true";
87
+ }
88
+ function copyDir(src, dest) {
89
+ import_fs.default.mkdirSync(dest, { recursive: true });
90
+ const entries = import_fs.default.readdirSync(src, { withFileTypes: true });
91
+ for (const entry of entries) {
92
+ const srcPath = import_path.default.join(src, entry.name);
93
+ const destPath = import_path.default.join(dest, entry.name);
94
+ if (entry.isDirectory()) {
95
+ copyDir(srcPath, destPath);
96
+ } else {
97
+ import_fs.default.copyFileSync(srcPath, destPath);
98
+ }
99
+ }
100
+ }
101
+ function ensureDir(dir) {
102
+ if (!import_fs.default.existsSync(dir)) {
103
+ import_fs.default.mkdirSync(dir, { recursive: true });
104
+ }
105
+ }
106
+ function removeDir(dir) {
107
+ if (import_fs.default.existsSync(dir)) {
108
+ import_fs.default.rmSync(dir, { recursive: true, force: true });
109
+ }
110
+ }
111
+ function readSkillConfig(dir) {
112
+ const configPath = import_path.default.join(dir, ".claude-skill.json");
113
+ if (!import_fs.default.existsSync(configPath)) {
114
+ throw new Error(".claude-skill.json not found");
115
+ }
116
+ return JSON.parse(import_fs.default.readFileSync(configPath, "utf-8"));
117
+ }
118
+
119
+ // shared/src/install-skill.ts
120
+ function fetchFromRemote(tempDir, remoteSource) {
121
+ try {
122
+ console.log(` \u{1F310} Fetching latest from ${remoteSource}...`);
123
+ (0, import_child_process.execSync)(`npx degit ${remoteSource} "${tempDir}" --force`, {
124
+ stdio: "pipe",
125
+ timeout: 6e4
126
+ });
127
+ if (import_fs2.default.existsSync(import_path2.default.join(tempDir, "SKILL.md"))) {
128
+ console.log(" \u2713 Fetched latest version from remote");
129
+ return true;
130
+ }
131
+ console.warn(" \u26A0 Remote fetch succeeded but SKILL.md not found");
132
+ return false;
133
+ } catch (error) {
134
+ const message = error instanceof Error ? error.message : String(error);
135
+ console.warn(` \u26A0 Could not fetch from remote: ${message}`);
136
+ console.log(" \u2139 Using bundled version as fallback");
137
+ return false;
138
+ }
139
+ }
140
+ function getSourceDir(config, packageDir) {
141
+ if (!config.remoteSource) {
142
+ return {
143
+ sourceDir: packageDir,
144
+ cleanup: () => {
145
+ },
146
+ isRemote: false
147
+ };
148
+ }
149
+ const tempDir = import_path2.default.join(import_os2.default.tmpdir(), `skill-fetch-${Date.now()}`);
150
+ const remoteSuccess = fetchFromRemote(tempDir, config.remoteSource);
151
+ if (remoteSuccess) {
152
+ return {
153
+ sourceDir: tempDir,
154
+ cleanup: () => {
155
+ try {
156
+ import_fs2.default.rmSync(tempDir, { recursive: true, force: true });
157
+ } catch {
158
+ }
159
+ },
160
+ isRemote: true
161
+ };
162
+ }
163
+ try {
164
+ import_fs2.default.rmSync(tempDir, { recursive: true, force: true });
165
+ } catch {
166
+ }
167
+ return {
168
+ sourceDir: packageDir,
169
+ cleanup: () => {
170
+ },
171
+ isRemote: false
172
+ };
173
+ }
174
+ function updateManifest(skillsDir, config, targetName, isRemote) {
175
+ const manifestPath = import_path2.default.join(skillsDir, ".skills-manifest.json");
176
+ let manifest = { skills: {} };
177
+ if (import_fs2.default.existsSync(manifestPath)) {
178
+ try {
179
+ manifest = JSON.parse(import_fs2.default.readFileSync(manifestPath, "utf-8"));
180
+ } catch {
181
+ console.warn(" Warning: Could not parse existing manifest, creating new one");
182
+ manifest = { skills: {} };
183
+ }
184
+ }
185
+ const skillName = extractSkillName(config.name);
186
+ manifest.skills[config.name] = {
187
+ version: config.version,
188
+ installedAt: (/* @__PURE__ */ new Date()).toISOString(),
189
+ package: config.package || config.name,
190
+ path: import_path2.default.join(skillsDir, skillName),
191
+ target: targetName,
192
+ ...config.remoteSource && { source: config.remoteSource }
193
+ };
194
+ import_fs2.default.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
195
+ }
196
+ function installToTarget(target, config, sourceDir, isRemote) {
197
+ var _a;
198
+ console.log(`
199
+ \u{1F4E6} Installing to ${target.name}...`);
200
+ const isGlobal = isGlobalInstall();
201
+ const location = detectInstallLocation(target.paths, isGlobal);
202
+ const skillName = extractSkillName(config.name);
203
+ const targetDir = import_path2.default.join(location.base, skillName);
204
+ const altTargetDir = import_path2.default.join(location.base, config.name);
205
+ console.log(` Type: ${location.type}${isGlobal ? " (global)" : " (project)"}`);
206
+ console.log(` Directory: ${targetDir}`);
207
+ if (import_fs2.default.existsSync(altTargetDir) && altTargetDir !== targetDir) {
208
+ console.log(" \u{1F9F9} Cleaning up alternative path format...");
209
+ removeDir(altTargetDir);
210
+ console.log(` \u2713 Removed directory: ${config.name}`);
211
+ }
212
+ ensureDir(targetDir);
213
+ const skillMdSource = import_path2.default.join(sourceDir, "SKILL.md");
214
+ if (!import_fs2.default.existsSync(skillMdSource)) {
215
+ throw new Error("SKILL.md is required but not found");
216
+ }
217
+ import_fs2.default.copyFileSync(skillMdSource, import_path2.default.join(targetDir, "SKILL.md"));
218
+ console.log(" \u2713 Copied SKILL.md");
219
+ const filesToCopy = config.files || {};
220
+ for (const [source, dest] of Object.entries(filesToCopy)) {
221
+ const sourcePath = import_path2.default.join(sourceDir, source);
222
+ if (!import_fs2.default.existsSync(sourcePath)) {
223
+ console.warn(` \u26A0 Warning: ${source} not found, skipping`);
224
+ continue;
225
+ }
226
+ const destPath = import_path2.default.join(targetDir, dest);
227
+ if (import_fs2.default.statSync(sourcePath).isDirectory()) {
228
+ copyDir(sourcePath, destPath);
229
+ console.log(` \u2713 Copied directory: ${source}`);
230
+ } else {
231
+ const destDir = import_path2.default.dirname(destPath);
232
+ ensureDir(destDir);
233
+ import_fs2.default.copyFileSync(sourcePath, destPath);
234
+ console.log(` \u2713 Copied file: ${source}`);
235
+ }
236
+ }
237
+ updateManifest(location.base, config, target.name, isRemote);
238
+ if ((_a = config.hooks) == null ? void 0 : _a.postinstall) {
239
+ console.log(" \u{1F527} Running postinstall hook...");
240
+ try {
241
+ (0, import_child_process.execSync)(config.hooks.postinstall, {
242
+ cwd: targetDir,
243
+ stdio: "pipe"
244
+ });
245
+ console.log(" \u2713 Postinstall hook completed");
246
+ } catch {
247
+ console.warn(" \u26A0 Warning: postinstall hook failed");
248
+ }
249
+ }
250
+ console.log(` \u2705 Installed to ${target.name}`);
251
+ return targetDir;
252
+ }
253
+ function installSkill() {
254
+ console.log("\u{1F680} Installing AI Coding Skill...\n");
255
+ const packageDir = __dirname;
256
+ const config = readSkillConfig(packageDir);
257
+ const enabledTargets = getEnabledTargets(config);
258
+ if (enabledTargets.length === 0) {
259
+ console.warn("\u26A0 No targets enabled in configuration");
260
+ console.warn("Please enable at least one target in .claude-skill.json");
261
+ return;
262
+ }
263
+ console.log(`Installing skill "${config.name}" to ${enabledTargets.length} target(s):`);
264
+ enabledTargets.forEach((target) => {
265
+ console.log(` \u2022 ${target.name}`);
266
+ });
267
+ const { sourceDir, cleanup, isRemote } = getSourceDir(config, packageDir);
268
+ if (isRemote) {
269
+ console.log(`
270
+ \u{1F4E1} Source: Remote (${config.remoteSource})`);
271
+ } else {
272
+ console.log("\n\u{1F4E6} Source: Bundled (local)");
273
+ }
274
+ try {
275
+ const installedPaths = [];
276
+ for (const target of enabledTargets) {
277
+ try {
278
+ const installPath = installToTarget(target, config, sourceDir, isRemote);
279
+ installedPaths.push({ target: target.name, path: installPath });
280
+ } catch (error) {
281
+ const message = error instanceof Error ? error.message : String(error);
282
+ console.error(`
283
+ \u274C Failed to install to ${target.name}:`, message);
284
+ }
285
+ }
286
+ console.log("\n" + "=".repeat(60));
287
+ console.log("\u2705 Installation Complete!");
288
+ console.log("=".repeat(60));
289
+ if (installedPaths.length > 0) {
290
+ console.log("\nInstalled to:");
291
+ installedPaths.forEach(({ target, path: installPath }) => {
292
+ console.log(` \u2022 ${target}: ${installPath}`);
293
+ });
294
+ console.log("\n\u{1F4D6} Next Steps:");
295
+ console.log(" 1. Restart your AI coding tool(s)");
296
+ console.log(' 2. Ask: "What skills are available?"');
297
+ console.log(" 3. Start using your skill!");
298
+ }
299
+ } finally {
300
+ cleanup();
301
+ }
302
+ }
303
+ try {
304
+ installSkill();
305
+ } catch (error) {
306
+ const message = error instanceof Error ? error.message : String(error);
307
+ console.error("\n\u274C Failed to install skill:", message);
308
+ console.error("\nTroubleshooting:");
309
+ console.error("- Ensure .claude-skill.json exists and is valid JSON");
310
+ console.error("- Ensure SKILL.md exists");
311
+ console.error("- Check file permissions for target directories");
312
+ console.error("- Verify at least one target is enabled in .claude-skill.json");
313
+ console.error("- Try running with sudo for global installation (if needed)");
314
+ process.exit(1);
315
+ }
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@adonis0123/commit",
3
+ "version": "1.0.1",
4
+ "description": "Claude Code Skill - 根据暂存的代码变更自动生成符合 Conventional Commits 规范的提交信息",
5
+ "scripts": {
6
+ "postinstall": "node install-skill.js",
7
+ "preuninstall": "node uninstall-skill.js",
8
+ "test": "node install-skill.js && echo 'Installation test completed.'"
9
+ },
10
+ "files": [
11
+ "SKILL.md",
12
+ "references/",
13
+ "install-skill.js",
14
+ "uninstall-skill.js",
15
+ ".claude-skill.json"
16
+ ],
17
+ "keywords": [
18
+ "claude-code",
19
+ "skill",
20
+ "commit",
21
+ "git",
22
+ "conventional-commits",
23
+ "emoji"
24
+ ],
25
+ "author": "adonis",
26
+ "license": "MIT",
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git+https://github.com/Adonis0123/agent-skill-npm-boilerplate.git",
30
+ "directory": "packages/commit"
31
+ },
32
+ "homepage": "https://github.com/Adonis0123/agent-skill-npm-boilerplate/tree/main/packages/commit"
33
+ }
@@ -0,0 +1,91 @@
1
+ # Commit Convention 完整规范
2
+
3
+ 本项目使用 Conventional Commits 规范,配合 Husky 和 Commitlint 进行自动化校验。
4
+
5
+ ## 工具链
6
+
7
+ | 工具 | 版本 | 用途 |
8
+ |------|------|------|
9
+ | husky | ^9.1.7 | Git hooks 管理 |
10
+ | lint-staged | ^15.2.10 | 暂存文件检查 |
11
+ | @commitlint/cli | ^19.8.1 | 提交消息格式校验 |
12
+ | @jannajs/lint | 3.1.3 | 提交消息 emoji 自动添加 |
13
+ | @jannajs/git-guards | ^0.0.8 | 合并守卫 |
14
+
15
+ ## 提交消息格式
16
+
17
+ ```
18
+ [emoji] type(scope): subject
19
+
20
+ body
21
+
22
+ footer
23
+ ```
24
+
25
+ ### Header 解析正则
26
+
27
+ ```javascript
28
+ /^(?:([^\w\s]{1,2})\s+)?(\w+)(?:\((.*)\))?: (.*)$/
29
+ ```
30
+
31
+ ### 规则限制
32
+
33
+ - `header-max-length`: 250 字符
34
+ - `body-max-line-length`: 300 字符
35
+
36
+ ## Emoji 映射表
37
+
38
+ | 类型 | Emoji | Unicode | 示例 |
39
+ |------|-------|---------|------|
40
+ | feat | ✨ | U+2728 | `✨ feat: add new feature` |
41
+ | fix | 🐛 | U+1F41B | `🐛 fix: resolve bug` |
42
+ | docs | 📝 | U+1F4DD | `📝 docs: update readme` |
43
+ | style | 🎨 | U+1F3A8 | `🎨 style: format code` |
44
+ | refactor | ♻️ | U+267B | `♻️ refactor: optimize structure` |
45
+ | perf | ⚡️ | U+26A1 | `⚡️ perf: improve performance` |
46
+ | test | ✅ | U+2705 | `✅ test: add unit tests` |
47
+ | build | 🏗️ | U+1F3D7 | `🏗️ build: update dependencies` |
48
+ | ci | 👷 | U+1F477 | `👷 ci: update workflows` |
49
+ | chore | 🔧 | U+1F527 | `🔧 chore: update configs` |
50
+
51
+ ## Emoji 自动处理逻辑
52
+
53
+ `@jannajs/lint emojify` 会自动处理 emoji:
54
+
55
+ 1. 如果消息已有正确 emoji,保持不变
56
+ 2. 如果消息有错误 emoji,自动替换为正确的
57
+ 3. 如果消息没有 emoji,自动添加
58
+
59
+ ## 合并守卫规则
60
+
61
+ `@jannajs/git-guards merge` 检测并阻止不规范的合并:
62
+
63
+ - **黑名单检查**: 禁止从 `test`、`origin/test` 分支合并
64
+ - **当前分支检查**: 禁止创建当前分支的合并提交
65
+
66
+ **合并消息解析规则**:
67
+ ```javascript
68
+ /Merge branch '(.+?)'/i
69
+ /Merge remote-tracking branch '(.+?)'/i
70
+ ```
71
+
72
+ ## Pre-commit 钩子
73
+
74
+ 提交前会执行以下检查:
75
+
76
+ 1. **lint-staged** - 对暂存文件执行:
77
+ - `prettier --write --ignore-unknown` - 代码格式化
78
+ - `eslint --flag unstable_ts_config --fix` - ESLint 检查
79
+
80
+ 2. **typecheck:preview** - TypeScript 类型检查
81
+ - 运行 `tsgo --noEmit` 进行快速类型校验
82
+
83
+ ## 跳过钩子
84
+
85
+ ```bash
86
+ # 临时跳过(不推荐)
87
+ HUSKY=0 git commit -m "message"
88
+
89
+ # 或使用 --no-verify
90
+ git commit --no-verify -m "message"
91
+ ```
@@ -0,0 +1,40 @@
1
+ # Commit Message Examples
2
+
3
+ ## feat - 新功能
4
+
5
+ ```bash
6
+ # 基本格式
7
+ ✨ feat: add user authentication
8
+
9
+ # 带 scope
10
+ ✨ feat(auth): implement JWT token refresh
11
+
12
+ # 带 body
13
+ ✨ feat(api): add pagination support for user list
14
+
15
+ Implement cursor-based pagination for better performance
16
+ with large datasets.
17
+ ```
18
+
19
+ ## fix - Bug 修复
20
+
21
+ ```bash
22
+ # 基本格式
23
+ 🐛 fix: resolve login timeout issue
24
+
25
+ # 带 scope
26
+ 🐛 fix(ui): correct button alignment on mobile
27
+
28
+ # 带 issue 引用
29
+ 🐛 fix(payment): handle edge case in refund calculation
30
+
31
+ Closes #123
32
+ ```
33
+
34
+ ## docs - 文档变更
35
+
36
+ ```bash
37
+ 📝 docs: update API documentation
38
+ 📝 docs(readme): add installation instructions
39
+ 📝 docs(contributing): clarify PR process
40
+ ```
@@ -0,0 +1,191 @@
1
+ #!/usr/bin/env node
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (let key of __getOwnPropNames(from))
11
+ if (!__hasOwnProp.call(to, key) && key !== except)
12
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
+ }
14
+ return to;
15
+ };
16
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
+ // If the importer is in node compatibility mode or this is not an ESM
18
+ // file that has been converted to a CommonJS file using a Babel-
19
+ // compatible transform (i.e. "__esModule" has not been set), then set
20
+ // "default" to the CommonJS "module.exports" for node compatibility.
21
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
+ mod
23
+ ));
24
+
25
+ // shared/src/uninstall-skill.ts
26
+ var import_fs2 = __toESM(require("fs"));
27
+ var import_path2 = __toESM(require("path"));
28
+
29
+ // shared/src/utils.ts
30
+ var import_fs = __toESM(require("fs"));
31
+ var import_path = __toESM(require("path"));
32
+ var import_os = __toESM(require("os"));
33
+ var CWD = process.env.INIT_CWD || process.cwd();
34
+ var DEFAULT_TARGET = {
35
+ name: "claude-code",
36
+ paths: {
37
+ global: ".claude/skills",
38
+ project: ".claude/skills"
39
+ }
40
+ };
41
+ function getEnabledTargets(config) {
42
+ if (!config.targets) {
43
+ return [DEFAULT_TARGET];
44
+ }
45
+ return Object.entries(config.targets).filter(([_, target]) => target.enabled).map(([name, target]) => ({
46
+ name,
47
+ paths: target.paths
48
+ }));
49
+ }
50
+ function extractSkillName(packageName) {
51
+ if (packageName.startsWith("@")) {
52
+ return packageName.split("/")[1] || packageName;
53
+ }
54
+ return packageName;
55
+ }
56
+ function detectInstallLocation(targetPaths, isGlobal) {
57
+ if (isGlobal) {
58
+ return {
59
+ type: "personal",
60
+ base: import_path.default.join(import_os.default.homedir(), targetPaths.global)
61
+ };
62
+ }
63
+ let projectRoot = CWD;
64
+ while (projectRoot !== import_path.default.dirname(projectRoot)) {
65
+ const hasPackageJson = import_fs.default.existsSync(import_path.default.join(projectRoot, "package.json"));
66
+ const hasGit = import_fs.default.existsSync(import_path.default.join(projectRoot, ".git"));
67
+ const isInNodeModules = projectRoot.includes("/node_modules/") || import_path.default.basename(projectRoot) === "node_modules";
68
+ if ((hasPackageJson || hasGit) && !isInNodeModules) {
69
+ break;
70
+ }
71
+ projectRoot = import_path.default.dirname(projectRoot);
72
+ }
73
+ const finalIsInNodeModules = projectRoot.includes("/node_modules/") || import_path.default.basename(projectRoot) === "node_modules";
74
+ if (finalIsInNodeModules) {
75
+ console.warn("\u26A0 Warning: Could not find project root directory, using current directory");
76
+ projectRoot = CWD;
77
+ }
78
+ return {
79
+ type: "project",
80
+ base: import_path.default.join(projectRoot, targetPaths.project)
81
+ };
82
+ }
83
+ function isGlobalInstall() {
84
+ return process.env.npm_config_global === "true";
85
+ }
86
+ function removeDir(dir) {
87
+ if (import_fs.default.existsSync(dir)) {
88
+ import_fs.default.rmSync(dir, { recursive: true, force: true });
89
+ }
90
+ }
91
+ function readSkillConfig(dir) {
92
+ const configPath = import_path.default.join(dir, ".claude-skill.json");
93
+ if (!import_fs.default.existsSync(configPath)) {
94
+ throw new Error(".claude-skill.json not found");
95
+ }
96
+ return JSON.parse(import_fs.default.readFileSync(configPath, "utf-8"));
97
+ }
98
+
99
+ // shared/src/uninstall-skill.ts
100
+ function updateManifest(skillsDir, config) {
101
+ const manifestPath = import_path2.default.join(skillsDir, ".skills-manifest.json");
102
+ if (!import_fs2.default.existsSync(manifestPath)) {
103
+ return;
104
+ }
105
+ try {
106
+ const manifest = JSON.parse(import_fs2.default.readFileSync(manifestPath, "utf-8"));
107
+ if (manifest.skills && manifest.skills[config.name]) {
108
+ delete manifest.skills[config.name];
109
+ import_fs2.default.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
110
+ console.log(" \u2713 Updated manifest");
111
+ }
112
+ } catch (error) {
113
+ const message = error instanceof Error ? error.message : String(error);
114
+ console.warn(" Warning: Could not update manifest:", message);
115
+ }
116
+ }
117
+ function uninstallFromTarget(target, config) {
118
+ console.log(`
119
+ \u{1F5D1}\uFE0F Uninstalling from ${target.name}...`);
120
+ const isGlobal = isGlobalInstall();
121
+ const location = detectInstallLocation(target.paths, isGlobal);
122
+ const skillName = extractSkillName(config.name);
123
+ const skillNameTargetDir = import_path2.default.join(location.base, skillName);
124
+ const fullPackageNameTargetDir = import_path2.default.join(location.base, config.name);
125
+ let removed = false;
126
+ if (import_fs2.default.existsSync(skillNameTargetDir)) {
127
+ removeDir(skillNameTargetDir);
128
+ console.log(` \u2713 Removed skill directory: ${skillName}`);
129
+ removed = true;
130
+ }
131
+ if (import_fs2.default.existsSync(fullPackageNameTargetDir) && fullPackageNameTargetDir !== skillNameTargetDir) {
132
+ removeDir(fullPackageNameTargetDir);
133
+ console.log(` \u2713 Removed skill directory: ${config.name}`);
134
+ removed = true;
135
+ }
136
+ updateManifest(location.base, config);
137
+ if (removed) {
138
+ console.log(` \u2705 Uninstalled from ${target.name}`);
139
+ return true;
140
+ } else {
141
+ console.log(` \u2139\uFE0F Skill was not installed in ${target.name}`);
142
+ return false;
143
+ }
144
+ }
145
+ function uninstallSkill() {
146
+ console.log("\u{1F5D1}\uFE0F Uninstalling AI Coding Skill...\n");
147
+ const packageDir = __dirname;
148
+ let config;
149
+ try {
150
+ config = readSkillConfig(packageDir);
151
+ } catch {
152
+ console.warn("Warning: .claude-skill.json not found, skipping cleanup");
153
+ return;
154
+ }
155
+ const enabledTargets = getEnabledTargets(config);
156
+ console.log(`Uninstalling skill "${config.name}" from ${enabledTargets.length} target(s):`);
157
+ enabledTargets.forEach((target) => {
158
+ console.log(` \u2022 ${target.name}`);
159
+ });
160
+ const uninstalledFrom = [];
161
+ for (const target of enabledTargets) {
162
+ try {
163
+ const success = uninstallFromTarget(target, config);
164
+ if (success) {
165
+ uninstalledFrom.push(target.name);
166
+ }
167
+ } catch (error) {
168
+ const message = error instanceof Error ? error.message : String(error);
169
+ console.error(`
170
+ \u274C Failed to uninstall from ${target.name}:`, message);
171
+ }
172
+ }
173
+ console.log("\n" + "=".repeat(60));
174
+ if (uninstalledFrom.length > 0) {
175
+ console.log("\u2705 Uninstallation Complete!");
176
+ console.log("=".repeat(60));
177
+ console.log("\nUninstalled from:");
178
+ uninstalledFrom.forEach((target) => {
179
+ console.log(` \u2022 ${target}`);
180
+ });
181
+ } else {
182
+ console.log("\u2139\uFE0F Skill was not installed");
183
+ console.log("=".repeat(60));
184
+ }
185
+ }
186
+ try {
187
+ uninstallSkill();
188
+ } catch (error) {
189
+ const message = error instanceof Error ? error.message : String(error);
190
+ console.error("\n\u26A0\uFE0F Warning during uninstall:", message);
191
+ }