@shelken/pi-co-authored-by 0.2.7 → 0.2.9
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 +19 -5
- package/lib/commit.ts +121 -32
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
# pi-co-authored-by
|
|
2
2
|
|
|
3
|
-
agent
|
|
3
|
+
agent 用 bash 跑 `git commit` 时,自动追加 trailer:
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
-
|
|
5
|
+
```text
|
|
6
|
+
Co-Authored-By: <model> <noreply@pi.dev>
|
|
7
|
+
Generated-By: pi <version>
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
实现:bash 期间在真实 hooks 目录临时安装 `prepare-commit-msg`(不改 `core.hooksPath`);pid 引用计数,shell 退出时最后一个引用卸装并恢复原 hook。
|
|
11
|
+
|
|
12
|
+
初始 fork 自 [bruno-garcia/pi-co-authored-by](https://github.com/bruno-garcia/pi-co-authored-by)。
|
|
7
13
|
|
|
8
|
-
|
|
14
|
+
## 功能
|
|
9
15
|
|
|
10
|
-
|
|
16
|
+
- 只挂钩 `bash` 工具调用
|
|
17
|
+
- 模型名取当前 session;没有则 `unknown`
|
|
18
|
+
- 与仓库已有 hook 兼容(先跑临时 hook,再链式原 hook)
|
|
11
19
|
|
|
12
20
|
## 安装
|
|
13
21
|
|
|
@@ -15,6 +23,12 @@ agent 通过 bash 工具执行 `git commit` 时,自动追加:
|
|
|
15
23
|
pi install npm:@shelken/pi-co-authored-by
|
|
16
24
|
```
|
|
17
25
|
|
|
26
|
+
装好后 `/reload`。之后 agent 提交即可,无需额外命令。
|
|
27
|
+
|
|
28
|
+
## 配置
|
|
29
|
+
|
|
30
|
+
无配置文件。
|
|
31
|
+
|
|
18
32
|
## 验证
|
|
19
33
|
|
|
20
34
|
```bash
|
package/lib/commit.ts
CHANGED
|
@@ -20,7 +20,10 @@ export function removeCommitHookDirectory(hooksDir: string | undefined): void {
|
|
|
20
20
|
rmSync(hooksDir, { recursive: true, force: true });
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* 注入 trailer 元数据,并在当前仓库临时安装 prepare-commit-msg。
|
|
25
|
+
* 不设置 core.hooksPath,避免污染 git config;shell 退出时恢复原 hook。
|
|
26
|
+
*/
|
|
24
27
|
export function wrapBashWithCommitHook(
|
|
25
28
|
cmd: string,
|
|
26
29
|
hooksDir: string,
|
|
@@ -38,24 +41,129 @@ function buildEnvironmentPrefix(
|
|
|
38
41
|
coAuthor: string,
|
|
39
42
|
generatedBy: string,
|
|
40
43
|
): string {
|
|
41
|
-
|
|
42
|
-
export
|
|
43
|
-
export PI_CO_AUTHORED_BY_CO_AUTHOR=${shellQuote(coAuthor)}
|
|
44
|
+
const hookSrc = join(hooksDir, "prepare-commit-msg");
|
|
45
|
+
return `export PI_CO_AUTHORED_BY_CO_AUTHOR=${shellQuote(coAuthor)}
|
|
44
46
|
export PI_CO_AUTHORED_BY_GENERATED_BY=${shellQuote(generatedBy)}
|
|
45
|
-
export
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
export PI_CO_AUTHORED_BY_HOOK_SRC=${shellQuote(hookSrc)}
|
|
48
|
+
|
|
49
|
+
__pi_co_authored_by_installed_hooks=""
|
|
50
|
+
|
|
51
|
+
__pi_co_authored_by_resolve_hooks_dir() {
|
|
52
|
+
local configured hooks_dir toplevel
|
|
53
|
+
configured="$(command git config --get core.hooksPath 2>/dev/null || true)"
|
|
54
|
+
if [ -n "$configured" ]; then
|
|
55
|
+
case "$configured" in
|
|
56
|
+
/*) hooks_dir="$configured" ;;
|
|
57
|
+
*)
|
|
58
|
+
toplevel="$(command git rev-parse --show-toplevel 2>/dev/null)" || return 1
|
|
59
|
+
hooks_dir="$toplevel/$configured"
|
|
60
|
+
;;
|
|
61
|
+
esac
|
|
62
|
+
else
|
|
63
|
+
hooks_dir="$(command git rev-parse --git-path hooks 2>/dev/null)" || return 1
|
|
64
|
+
fi
|
|
65
|
+
# 绝对路径,避免 cwd 变化后 cleanup 找不到
|
|
66
|
+
case "$hooks_dir" in
|
|
67
|
+
/*) ;;
|
|
68
|
+
*) hooks_dir="$(CDPATH= cd -- "$hooks_dir" 2>/dev/null && pwd)" || return 1 ;;
|
|
69
|
+
esac
|
|
70
|
+
printf '%s' "$hooks_dir"
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
# 清掉已死进程的 ref,避免 SIGKILL 后永远占坑
|
|
74
|
+
__pi_co_authored_by_prune_refs() {
|
|
75
|
+
local refs_dir="$1" f pid
|
|
76
|
+
[ -d "$refs_dir" ] || return 0
|
|
77
|
+
for f in "$refs_dir"/*; do
|
|
78
|
+
[ -e "$f" ] || continue
|
|
79
|
+
pid="\${f##*/}"
|
|
80
|
+
case "$pid" in
|
|
81
|
+
''|*[!0-9]*) rm -f "$f" ;;
|
|
82
|
+
*) kill -0 "$pid" 2>/dev/null || rm -f "$f" ;;
|
|
83
|
+
esac
|
|
84
|
+
done
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
# 登记本 shell 对 hooks_dir 的引用(见 marker 也要登记,否则 sticky dirty)
|
|
88
|
+
__pi_co_authored_by_register() {
|
|
89
|
+
local hooks_dir="$1" refs_dir
|
|
90
|
+
refs_dir="$hooks_dir/.pi-co-authored-by-refs"
|
|
91
|
+
mkdir -p "$refs_dir" 2>/dev/null || return 0
|
|
92
|
+
__pi_co_authored_by_prune_refs "$refs_dir"
|
|
93
|
+
: > "$refs_dir/$$" 2>/dev/null || return 0
|
|
94
|
+
case "
|
|
95
|
+
$__pi_co_authored_by_installed_hooks
|
|
96
|
+
" in
|
|
97
|
+
*"
|
|
98
|
+
$hooks_dir
|
|
99
|
+
"*) ;;
|
|
100
|
+
*)
|
|
101
|
+
__pi_co_authored_by_installed_hooks="$__pi_co_authored_by_installed_hooks
|
|
102
|
+
$hooks_dir"
|
|
103
|
+
;;
|
|
104
|
+
esac
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
__pi_co_authored_by_ensure_hook() {
|
|
108
|
+
local hooks_dir hook
|
|
109
|
+
hooks_dir="$(__pi_co_authored_by_resolve_hooks_dir)" || return 0
|
|
110
|
+
hook="$hooks_dir/prepare-commit-msg"
|
|
111
|
+
mkdir -p "$hooks_dir" 2>/dev/null || return 0
|
|
112
|
+
|
|
113
|
+
if [ -f "$hook" ] && grep -q 'PI_CO_AUTHORED_BY_HOOK_MARKER' "$hook" 2>/dev/null; then
|
|
114
|
+
__pi_co_authored_by_register "$hooks_dir"
|
|
115
|
+
return 0
|
|
116
|
+
fi
|
|
117
|
+
|
|
118
|
+
if [ -e "$hook" ] && [ ! -e "$hooks_dir/prepare-commit-msg.pi-user" ]; then
|
|
119
|
+
mv "$hook" "$hooks_dir/prepare-commit-msg.pi-user"
|
|
120
|
+
fi
|
|
121
|
+
|
|
122
|
+
cp "$PI_CO_AUTHORED_BY_HOOK_SRC" "$hook"
|
|
123
|
+
chmod +x "$hook"
|
|
124
|
+
__pi_co_authored_by_register "$hooks_dir"
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
__pi_co_authored_by_cleanup() {
|
|
128
|
+
local hooks_dir hook refs_dir
|
|
129
|
+
printf '%s\n' "$__pi_co_authored_by_installed_hooks" | while IFS= read -r hooks_dir; do
|
|
130
|
+
[ -n "$hooks_dir" ] || continue
|
|
131
|
+
refs_dir="$hooks_dir/.pi-co-authored-by-refs"
|
|
132
|
+
rm -f "$refs_dir/$$" 2>/dev/null || true
|
|
133
|
+
__pi_co_authored_by_prune_refs "$refs_dir"
|
|
134
|
+
# 仍有活进程占用:只退自己的 ref,不卸 hook
|
|
135
|
+
if [ -n "$(ls -A "$refs_dir" 2>/dev/null)" ]; then
|
|
136
|
+
continue
|
|
137
|
+
fi
|
|
138
|
+
rmdir "$refs_dir" 2>/dev/null || true
|
|
139
|
+
hook="$hooks_dir/prepare-commit-msg"
|
|
140
|
+
if [ -f "$hook" ] && grep -q 'PI_CO_AUTHORED_BY_HOOK_MARKER' "$hook" 2>/dev/null; then
|
|
141
|
+
rm -f "$hook"
|
|
142
|
+
if [ -e "$hooks_dir/prepare-commit-msg.pi-user" ]; then
|
|
143
|
+
mv "$hooks_dir/prepare-commit-msg.pi-user" "$hook"
|
|
144
|
+
fi
|
|
145
|
+
fi
|
|
146
|
+
done
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
trap '__pi_co_authored_by_cleanup' EXIT
|
|
150
|
+
__pi_co_authored_by_ensure_hook
|
|
151
|
+
|
|
152
|
+
git() {
|
|
153
|
+
__pi_co_authored_by_ensure_hook
|
|
154
|
+
command git "$@"
|
|
155
|
+
}`;
|
|
49
156
|
}
|
|
50
157
|
|
|
51
158
|
function buildPrepareCommitMsgHook(): string {
|
|
52
159
|
return `#!/bin/sh
|
|
160
|
+
# PI_CO_AUTHORED_BY_HOOK_MARKER
|
|
53
161
|
set -u
|
|
54
162
|
|
|
55
163
|
message_file="$1"
|
|
56
164
|
|
|
57
165
|
if [ -n "\${PI_CO_AUTHORED_BY_CO_AUTHOR:-}" ] && [ -n "\${PI_CO_AUTHORED_BY_GENERATED_BY:-}" ]; then
|
|
58
|
-
git \
|
|
166
|
+
command git \
|
|
59
167
|
-c trailer.co-authored-by.ifExists=addIfDifferent \
|
|
60
168
|
-c trailer.generated-by.ifExists=replace \
|
|
61
169
|
interpret-trailers \
|
|
@@ -65,25 +173,10 @@ if [ -n "\${PI_CO_AUTHORED_BY_CO_AUTHOR:-}" ] && [ -n "\${PI_CO_AUTHORED_BY_GENE
|
|
|
65
173
|
"$message_file"
|
|
66
174
|
fi
|
|
67
175
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
export GIT_CONFIG_COUNT="$__pi_config_index"
|
|
73
|
-
fi
|
|
74
|
-
|
|
75
|
-
original_hooks_path="$(git config --get core.hooksPath || true)"
|
|
76
|
-
if [ -n "$original_hooks_path" ]; then
|
|
77
|
-
case "$original_hooks_path" in
|
|
78
|
-
/*) original_hook="$original_hooks_path/prepare-commit-msg" ;;
|
|
79
|
-
*) original_hook="$(git rev-parse --show-toplevel)/$original_hooks_path/prepare-commit-msg" ;;
|
|
80
|
-
esac
|
|
81
|
-
else
|
|
82
|
-
original_hook="$(git rev-parse --git-path hooks/prepare-commit-msg)"
|
|
83
|
-
fi
|
|
84
|
-
|
|
85
|
-
if [ -x "$original_hook" ] && [ "$original_hook" != "$0" ]; then
|
|
86
|
-
"$original_hook" "$@"
|
|
176
|
+
hooks_dir="$(dirname "$0")"
|
|
177
|
+
user_hook="$hooks_dir/prepare-commit-msg.pi-user"
|
|
178
|
+
if [ -x "$user_hook" ]; then
|
|
179
|
+
"$user_hook" "$@"
|
|
87
180
|
fi
|
|
88
181
|
`;
|
|
89
182
|
}
|
|
@@ -91,7 +184,3 @@ fi
|
|
|
91
184
|
function shellQuote(value: string): string {
|
|
92
185
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
93
186
|
}
|
|
94
|
-
|
|
95
|
-
function escapeDoubleQuotedAssignmentValue(value: string): string {
|
|
96
|
-
return value.replace(/[\\"$`]/g, "\\$&");
|
|
97
|
-
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shelken/pi-co-authored-by",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
4
4
|
"description": "agent 执行 git commit 时自动追加 Co-Authored-By / Generated-By trailer",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
]
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
|
-
"@earendil-works/pi-coding-agent": "
|
|
33
|
+
"@earendil-works/pi-coding-agent": ">=0.80.0"
|
|
34
34
|
},
|
|
35
35
|
"peerDependenciesMeta": {
|
|
36
36
|
"@earendil-works/pi-coding-agent": {
|