agent-skill-installer 0.1.4 → 0.1.6
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/bin/cli.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const fs = require("node:fs/promises");
|
|
4
4
|
const path = require("node:path");
|
|
5
5
|
const os = require("node:os");
|
|
6
|
+
const readline = require("node:readline/promises");
|
|
6
7
|
|
|
7
8
|
const REPO_ROOT = path.resolve(__dirname, "..");
|
|
8
9
|
const SKILLS_SOURCE_DIR = path.join(REPO_ROOT, "skills");
|
|
@@ -21,7 +22,7 @@ Commands:
|
|
|
21
22
|
|
|
22
23
|
Options:
|
|
23
24
|
--dest <path> Custom destination directory.
|
|
24
|
-
--force Overwrite destination if a skill already exists.
|
|
25
|
+
--force Overwrite destination if a skill already exists (no prompt).
|
|
25
26
|
-h, --help Show this help.
|
|
26
27
|
`);
|
|
27
28
|
}
|
|
@@ -112,6 +113,33 @@ async function listSkills() {
|
|
|
112
113
|
}
|
|
113
114
|
}
|
|
114
115
|
|
|
116
|
+
async function confirmOverwrite(name, targetPath) {
|
|
117
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
118
|
+
throw new Error(
|
|
119
|
+
`Destination already exists for "${name}": ${targetPath} (non-interactive mode, use --force to overwrite)`
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const rl = readline.createInterface({
|
|
124
|
+
input: process.stdin,
|
|
125
|
+
output: process.stdout
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
const answer = (
|
|
130
|
+
await rl.question(
|
|
131
|
+
`Skill "${name}" already exists at ${targetPath}. Overwrite it? [y/N] `
|
|
132
|
+
)
|
|
133
|
+
)
|
|
134
|
+
.trim()
|
|
135
|
+
.toLowerCase();
|
|
136
|
+
|
|
137
|
+
return answer === "y" || answer === "yes";
|
|
138
|
+
} finally {
|
|
139
|
+
rl.close();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
115
143
|
async function installSkills(rawArgs) {
|
|
116
144
|
const parsed = parseInstallArgs(rawArgs);
|
|
117
145
|
if (parsed.help) {
|
|
@@ -137,13 +165,16 @@ async function installSkills(rawArgs) {
|
|
|
137
165
|
const to = path.join(parsed.dest, name);
|
|
138
166
|
const exists = await dirExists(to);
|
|
139
167
|
|
|
140
|
-
if (exists &&
|
|
141
|
-
|
|
142
|
-
`Destination already exists for "${name}": ${to} (use --force to overwrite)`
|
|
143
|
-
);
|
|
168
|
+
if (exists && parsed.force) {
|
|
169
|
+
await fs.rm(to, { recursive: true, force: true });
|
|
144
170
|
}
|
|
145
171
|
|
|
146
|
-
if (exists && parsed.force) {
|
|
172
|
+
if (exists && !parsed.force) {
|
|
173
|
+
const shouldOverwrite = await confirmOverwrite(name, to);
|
|
174
|
+
if (!shouldOverwrite) {
|
|
175
|
+
console.log(`Skipped "${name}" (kept existing skill).`);
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
147
178
|
await fs.rm(to, { recursive: true, force: true });
|
|
148
179
|
}
|
|
149
180
|
|
package/package.json
CHANGED
|
@@ -3,44 +3,46 @@ name: git-commit-helper
|
|
|
3
3
|
description: "严格生成与校验中文 Git 提交信息,固定格式为 type(scope): summary,type 仅允许 feat/fix/refactor/perf/style/test/docs/chore,summary 必须用祈使句、不超过 50 字符、不得以句号结尾且不得包含 and/&/multiple changes。使用该技能于编写提交说明、检查提交文本是否合规,或执行使用 codex (codex-ice@gmail.com) 身份的提交时。"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
统一使用作者身份:`codex <codex-ice@gmail.com>`。
|
|
6
|
+
生成中文提交信息,先校验再提交。统一使用作者身份:`codex <codex-ice@gmail.com>`。
|
|
8
7
|
|
|
9
8
|
## 工作流
|
|
10
9
|
|
|
11
|
-
1.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
1. 检查暂存区,只允许一个明确意图:
|
|
11
|
+
`git diff --cached --name-status`
|
|
12
|
+
2. 选择 `type`:`feat` `fix` `refactor` `perf` `style` `test` `docs` `chore`。
|
|
13
|
+
3. 写提交消息文件,固定首行格式:
|
|
14
|
+
`<type>(<scope>): <summary>`
|
|
15
|
+
4. 若 `type=perf`,在空行后写正文并说明优化原因。
|
|
16
|
+
5. 执行校验:
|
|
17
|
+
`python3 skills/git-commit-helper/scripts/validate_commit_message.py --file <msg-file>`
|
|
18
|
+
6. 校验通过后提交:
|
|
19
|
+
`git -c user.name='codex' -c user.email='codex-ice@gmail.com' commit -F <msg-file>`
|
|
18
20
|
|
|
19
21
|
## 强制规则
|
|
20
22
|
|
|
21
|
-
1.
|
|
22
|
-
2. `
|
|
23
|
-
3. `summary`
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
4. 提交文本(`summary` 与可选正文)使用中文。
|
|
30
|
-
5. `perf` 类型必须在正文说明优化原因。
|
|
23
|
+
1. 首行必须是:`<type>(<scope>): <summary>`。
|
|
24
|
+
2. `scope` 只允许小写字母、数字、连字符(例如 `album`、`build-cache`)。
|
|
25
|
+
3. `summary` 必须使用中文祈使句,且不超过 50 字符。
|
|
26
|
+
4. `summary` 不能以 `。` 或 `.` 结尾。
|
|
27
|
+
5. `summary` 不能包含 `and`、`&`、`multiple changes`。
|
|
28
|
+
6. `summary` 与正文必须使用中文表达。
|
|
29
|
+
7. 若包含正文,第 2 行必须是空行。
|
|
30
|
+
8. `perf` 必须包含正文说明优化原因。
|
|
31
31
|
|
|
32
|
-
##
|
|
32
|
+
## 快速模板
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
- `docs(api): 补充鉴权参数说明`
|
|
34
|
+
```text
|
|
35
|
+
fix(scope): 优化某个单一行为
|
|
37
36
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
- `fix bug and refactor logic`
|
|
41
|
-
- `optimize`
|
|
37
|
+
仅在需要时写正文,说明原因或背景
|
|
38
|
+
```
|
|
42
39
|
|
|
43
|
-
##
|
|
40
|
+
## 失败处理
|
|
44
41
|
|
|
45
|
-
|
|
46
|
-
|
|
42
|
+
1. 阅读脚本返回的逐条错误。
|
|
43
|
+
2. 仅修改被指出的问题,避免一次改多个意图。
|
|
44
|
+
3. 重新执行校验,直到输出“校验通过”。
|
|
45
|
+
|
|
46
|
+
## 资源
|
|
47
|
+
|
|
48
|
+
- `skills/git-commit-helper/scripts/validate_commit_message.py`:提交信息规则校验器
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
interface:
|
|
2
2
|
display_name: "git-commit-helper"
|
|
3
|
-
short_description: "
|
|
4
|
-
default_prompt: "Use $git-commit-helper to draft a Chinese commit message
|
|
3
|
+
short_description: "严格生成并校验中文 Git 提交信息"
|
|
4
|
+
default_prompt: "Use $git-commit-helper to draft a Chinese commit message in `type(scope): summary`, validate it with the bundled script, then commit as codex <codex-ice@gmail.com>."
|
|
@@ -19,6 +19,18 @@ FORBIDDEN_SUMMARY_PATTERNS = (
|
|
|
19
19
|
re.compile(r"&"),
|
|
20
20
|
re.compile(r"multiple changes", re.IGNORECASE),
|
|
21
21
|
)
|
|
22
|
+
PAST_TENSE_PREFIXES = (
|
|
23
|
+
"已",
|
|
24
|
+
"已经",
|
|
25
|
+
"完成",
|
|
26
|
+
"修复了",
|
|
27
|
+
"新增了",
|
|
28
|
+
"优化了",
|
|
29
|
+
"更新了",
|
|
30
|
+
"重构了",
|
|
31
|
+
"添加了",
|
|
32
|
+
"改了",
|
|
33
|
+
)
|
|
22
34
|
FORBIDDEN_ENDINGS = (".", "。")
|
|
23
35
|
MAX_SUMMARY_LEN = 50
|
|
24
36
|
|
|
@@ -49,36 +61,50 @@ def validate_commit_message(message: str) -> list[str]:
|
|
|
49
61
|
|
|
50
62
|
match = HEADER_RE.match(header)
|
|
51
63
|
commit_type = None
|
|
64
|
+
raw_summary = ""
|
|
52
65
|
summary = ""
|
|
53
66
|
if not match:
|
|
54
67
|
errors.append("首行格式必须为 <type>(<scope>): <summary>")
|
|
55
68
|
else:
|
|
56
69
|
commit_type = match.group("type")
|
|
57
|
-
|
|
70
|
+
raw_summary = match.group("summary")
|
|
71
|
+
summary = raw_summary.strip()
|
|
58
72
|
|
|
59
73
|
if len(lines) > 1 and lines[1] != "":
|
|
60
74
|
errors.append("若包含正文,第 2 行必须为空行")
|
|
61
75
|
|
|
62
|
-
|
|
76
|
+
body_lines = lines[2:] if len(lines) > 2 else []
|
|
77
|
+
body = "\n".join(body_lines).strip()
|
|
63
78
|
|
|
64
79
|
if commit_type and commit_type not in ALLOWED_TYPES:
|
|
65
80
|
errors.append("type 必须是 feat/fix/refactor/perf/style/test/docs/chore 之一")
|
|
66
81
|
|
|
82
|
+
if not summary and match:
|
|
83
|
+
errors.append("summary 不能为空")
|
|
84
|
+
|
|
67
85
|
if summary:
|
|
86
|
+
if raw_summary != summary:
|
|
87
|
+
errors.append("summary 前后不能有空格")
|
|
68
88
|
if len(summary) > MAX_SUMMARY_LEN:
|
|
69
89
|
errors.append("summary 长度不能超过 50 字符")
|
|
70
90
|
if summary.endswith(FORBIDDEN_ENDINGS):
|
|
71
91
|
errors.append("summary 结尾不能使用句号")
|
|
72
92
|
if not CJK_RE.search(summary):
|
|
73
93
|
errors.append("summary 必须使用中文")
|
|
94
|
+
if summary.startswith(PAST_TENSE_PREFIXES):
|
|
95
|
+
errors.append("summary 必须使用祈使句,避免使用“已/完成/xxx了”开头")
|
|
74
96
|
|
|
75
97
|
for pattern in FORBIDDEN_SUMMARY_PATTERNS:
|
|
76
98
|
if pattern.search(summary):
|
|
77
99
|
errors.append("summary 不能包含 and / & / multiple changes")
|
|
78
100
|
break
|
|
79
101
|
|
|
80
|
-
|
|
81
|
-
|
|
102
|
+
for line_no, line in enumerate(body_lines, start=3):
|
|
103
|
+
stripped = line.strip()
|
|
104
|
+
if not stripped:
|
|
105
|
+
continue
|
|
106
|
+
if not CJK_RE.search(stripped):
|
|
107
|
+
errors.append(f"正文第 {line_no} 行需使用中文")
|
|
82
108
|
|
|
83
109
|
if commit_type == "perf" and not body:
|
|
84
110
|
errors.append("perf 类型必须提供正文说明优化原因")
|