@andyqiu/teamkit 0.1.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 +73 -0
- package/agents/teamkit-git.md +42 -0
- package/bin/.gitkeep +0 -0
- package/bin/teamkit-doctor.mjs +320 -0
- package/bin/teamkit-upgrade.mjs +93 -0
- package/bin/teamkit.mjs +115 -0
- package/context-templates/.gitkeep +0 -0
- package/context-templates/git-instructions.md +69 -0
- package/context-templates/kh-instructions.md +109 -0
- package/dist/index.cjs +1773 -0
- package/install.sh +302 -0
- package/package.json +45 -0
- package/scripts/adr-check.mjs +368 -0
- package/scripts/adr-index-sync.mjs +133 -0
- package/scripts/capability-detect.mjs +231 -0
- package/scripts/merge-agents-md.mjs +201 -0
- package/scripts/publish.sh +184 -0
package/install.sh
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# install.sh — teamkit 安装脚本
|
|
3
|
+
#
|
|
4
|
+
# 四件事:
|
|
5
|
+
# 1. node scripts/capability-detect.mjs(hard fail 门)
|
|
6
|
+
# 2. 把 @andyqiu/teamkit 注册到 ~/.config/opencode/opencode.json plugins 数组(排首位)
|
|
7
|
+
# 3. 把 context-templates/kh-instructions.md 注入 ~/.config/opencode/AGENTS.md <!-- knowledge-hub:start/end -->
|
|
8
|
+
# 把 context-templates/git-instructions.md 注入 ~/.config/opencode/AGENTS.md <!-- teamkit-git:start/end -->
|
|
9
|
+
# 4. 把 agents/teamkit-git.md 复制到 ~/.config/opencode/agents/teamkit-git.md
|
|
10
|
+
#
|
|
11
|
+
# 支持 --dry-run 参数:打印将执行的操作但不实际写文件
|
|
12
|
+
#
|
|
13
|
+
# ADR: install-sh-wsl-path-guard
|
|
14
|
+
# ADR: opencode-version-compatibility
|
|
15
|
+
|
|
16
|
+
set -euo pipefail
|
|
17
|
+
|
|
18
|
+
# ────────────────────────────────────────────────────────────────────────────
|
|
19
|
+
# 颜色 & 输出工具
|
|
20
|
+
# ────────────────────────────────────────────────────────────────────────────
|
|
21
|
+
RED='\033[0;31m'
|
|
22
|
+
YELLOW='\033[1;33m'
|
|
23
|
+
GREEN='\033[0;32m'
|
|
24
|
+
CYAN='\033[0;36m'
|
|
25
|
+
NC='\033[0m' # No Color
|
|
26
|
+
|
|
27
|
+
err() { printf "${RED}❌ %s${NC}\n" "$*" >&2; }
|
|
28
|
+
warn() { printf "${YELLOW}⚠️ %s${NC}\n" "$*"; }
|
|
29
|
+
info() { printf "${CYAN}ℹ️ %s${NC}\n" "$*"; }
|
|
30
|
+
ok() { printf "${GREEN}✅ %s${NC}\n" "$*"; }
|
|
31
|
+
|
|
32
|
+
# ────────────────────────────────────────────────────────────────────────────
|
|
33
|
+
# 参数解析
|
|
34
|
+
# ────────────────────────────────────────────────────────────────────────────
|
|
35
|
+
DRY_RUN=0
|
|
36
|
+
|
|
37
|
+
for arg in "$@"; do
|
|
38
|
+
case "$arg" in
|
|
39
|
+
--dry-run) DRY_RUN=1 ;;
|
|
40
|
+
*)
|
|
41
|
+
err "未知参数: $arg"
|
|
42
|
+
printf "用法: install.sh [--dry-run]\n" >&2
|
|
43
|
+
exit 1
|
|
44
|
+
;;
|
|
45
|
+
esac
|
|
46
|
+
done
|
|
47
|
+
|
|
48
|
+
# ────────────────────────────────────────────────────────────────────────────
|
|
49
|
+
# 脚本所在目录(teamkit 包根)
|
|
50
|
+
# ────────────────────────────────────────────────────────────────────────────
|
|
51
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
52
|
+
|
|
53
|
+
# ────────────────────────────────────────────────────────────────────────────
|
|
54
|
+
# WSL 路径检查(ADR: install-sh-wsl-path-guard)
|
|
55
|
+
# 必须在所有路径解析之前执行
|
|
56
|
+
# ────────────────────────────────────────────────────────────────────────────
|
|
57
|
+
if command -v wslpath >/dev/null 2>&1 || [[ "${TEAMKIT_FORCE_WSL_CHECK:-}" == "1" ]]; then
|
|
58
|
+
case "$HOME" in
|
|
59
|
+
/mnt/c/*|/mnt/d/*|/mnt/e/*|/mnt/f/*|/mnt/g/*)
|
|
60
|
+
err "WSL 路径错位:\$HOME 落在 Windows NTFS 挂载路径 ($HOME)"
|
|
61
|
+
err "问题:teamkit 配置将写到 NTFS 文件系统,导致性能差 / 权限失效 / 路径混乱"
|
|
62
|
+
err "修复:在 /etc/wsl.conf 中设置 [user] default=<你的Linux用户名>,重启 WSL"
|
|
63
|
+
err "绕过:若你确实需要在 NTFS 路径运行,设置 TEAMKIT_ALLOW_WSL_WINDOWS_HOME=1"
|
|
64
|
+
if [[ "${TEAMKIT_ALLOW_WSL_WINDOWS_HOME:-}" != "1" ]]; then
|
|
65
|
+
exit 1
|
|
66
|
+
fi
|
|
67
|
+
warn "已通过 TEAMKIT_ALLOW_WSL_WINDOWS_HOME=1 跳过 WSL 路径检查"
|
|
68
|
+
;;
|
|
69
|
+
esac
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
# ────────────────────────────────────────────────────────────────────────────
|
|
73
|
+
# 路径配置
|
|
74
|
+
# ────────────────────────────────────────────────────────────────────────────
|
|
75
|
+
OPENCODE_CONFIG_DIR="${HOME}/.config/opencode"
|
|
76
|
+
OPENCODE_JSON="${OPENCODE_CONFIG_DIR}/opencode.json"
|
|
77
|
+
AGENTS_MD="${OPENCODE_CONFIG_DIR}/AGENTS.md"
|
|
78
|
+
KH_TEMPLATE="${SCRIPT_DIR}/context-templates/kh-instructions.md"
|
|
79
|
+
GIT_TEMPLATE="${SCRIPT_DIR}/context-templates/git-instructions.md"
|
|
80
|
+
MERGE_SCRIPT="${SCRIPT_DIR}/scripts/merge-agents-md.mjs"
|
|
81
|
+
|
|
82
|
+
# ────────────────────────────────────────────────────────────────────────────
|
|
83
|
+
# 前置检查
|
|
84
|
+
# ────────────────────────────────────────────────────────────────────────────
|
|
85
|
+
if ! command -v node >/dev/null 2>&1; then
|
|
86
|
+
err "Node.js 未安装,请先安装 Node.js >= 18"
|
|
87
|
+
exit 1
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
if [[ ! -f "${KH_TEMPLATE}" ]]; then
|
|
91
|
+
err "模板文件不存在: ${KH_TEMPLATE}"
|
|
92
|
+
exit 1
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
if [[ ! -f "${GIT_TEMPLATE}" ]]; then
|
|
96
|
+
err "模板文件不存在: ${GIT_TEMPLATE}"
|
|
97
|
+
exit 1
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
# ────────────────────────────────────────────────────────────────────────────
|
|
101
|
+
# STEP 0:capability detection(hard fail 门)
|
|
102
|
+
# ADR: opencode-version-compatibility §Decision §4
|
|
103
|
+
# ────────────────────────────────────────────────────────────────────────────
|
|
104
|
+
info "Step 0/3: 运行能力探测..."
|
|
105
|
+
|
|
106
|
+
if [[ "${DRY_RUN}" == "1" ]]; then
|
|
107
|
+
info "[dry-run] 将运行: node ${SCRIPT_DIR}/scripts/capability-detect.mjs"
|
|
108
|
+
else
|
|
109
|
+
if ! node "${SCRIPT_DIR}/scripts/capability-detect.mjs"; then
|
|
110
|
+
err "capability detection 失败,install 中止"
|
|
111
|
+
err "运行 \`teamkit doctor\` 获取详细诊断信息"
|
|
112
|
+
exit 1
|
|
113
|
+
fi
|
|
114
|
+
ok "能力探测通过"
|
|
115
|
+
fi
|
|
116
|
+
|
|
117
|
+
# ────────────────────────────────────────────────────────────────────────────
|
|
118
|
+
# STEP 1:注册 @andyqiu/teamkit 到 opencode.json plugins 数组(排首位)
|
|
119
|
+
# ────────────────────────────────────────────────────────────────────────────
|
|
120
|
+
info "Step 1/3: 注册 plugin entry 到 opencode.json..."
|
|
121
|
+
|
|
122
|
+
register_plugin_entry() {
|
|
123
|
+
local config_file="${OPENCODE_JSON}"
|
|
124
|
+
local plugin_name="@andyqiu/teamkit"
|
|
125
|
+
|
|
126
|
+
if [[ "${DRY_RUN}" == "1" ]]; then
|
|
127
|
+
info "[dry-run] 将把 ${plugin_name} 注册到 ${config_file} plugin 数组首位"
|
|
128
|
+
return 0
|
|
129
|
+
fi
|
|
130
|
+
|
|
131
|
+
# 确保配置目录存在
|
|
132
|
+
mkdir -p "${OPENCODE_CONFIG_DIR}"
|
|
133
|
+
|
|
134
|
+
# 用 Node.js 做 JSON 操作(避免 shell 解析 JSON 的各种坑)
|
|
135
|
+
# 通过 argv 传路径,Node.js 内自己读文件;heredoc 用单引号防止变量展开
|
|
136
|
+
node - "${config_file}" <<'NODEJS_EOF'
|
|
137
|
+
const fs = require('fs');
|
|
138
|
+
const path = process.argv[2];
|
|
139
|
+
let cfg = {};
|
|
140
|
+
try {
|
|
141
|
+
if (fs.existsSync(path)) cfg = JSON.parse(fs.readFileSync(path, 'utf8'));
|
|
142
|
+
} catch { cfg = {}; }
|
|
143
|
+
|
|
144
|
+
// opencode 用 "plugin"(单数),兼容读取两种 key
|
|
145
|
+
const existing = Array.isArray(cfg.plugin) ? cfg.plugin
|
|
146
|
+
: Array.isArray(cfg.plugins) ? cfg.plugins
|
|
147
|
+
: [];
|
|
148
|
+
|
|
149
|
+
const pluginName = '@andyqiu/teamkit';
|
|
150
|
+
const filtered = existing.filter(p => {
|
|
151
|
+
const name = typeof p === 'string' ? p : (p?.name ?? p?.path ?? '');
|
|
152
|
+
return !name.includes('teamkit');
|
|
153
|
+
});
|
|
154
|
+
filtered.unshift(pluginName);
|
|
155
|
+
|
|
156
|
+
// 统一写到 "plugin" key
|
|
157
|
+
cfg.plugin = filtered;
|
|
158
|
+
// 如果原来有 "plugins" key,同步删除避免混淆
|
|
159
|
+
if (cfg.plugins !== undefined) delete cfg.plugins;
|
|
160
|
+
|
|
161
|
+
fs.writeFileSync(path, JSON.stringify(cfg, null, 2) + '\n', 'utf8');
|
|
162
|
+
process.stdout.write(' ✓ 已注册 ' + pluginName + ' 到 plugin[0]\n');
|
|
163
|
+
NODEJS_EOF
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
register_plugin_entry
|
|
167
|
+
|
|
168
|
+
# ────────────────────────────────────────────────────────────────────────────
|
|
169
|
+
# STEP 2:注入 AGENTS.md
|
|
170
|
+
# ────────────────────────────────────────────────────────────────────────────
|
|
171
|
+
info "Step 2/3: 注入 AGENTS.md..."
|
|
172
|
+
|
|
173
|
+
inject_agents_md() {
|
|
174
|
+
if [[ "${DRY_RUN}" == "1" ]]; then
|
|
175
|
+
node "${MERGE_SCRIPT}" \
|
|
176
|
+
--target "${AGENTS_MD}" \
|
|
177
|
+
--template "${KH_TEMPLATE}" \
|
|
178
|
+
--dry-run
|
|
179
|
+
# git-instructions 注入(dry-run)
|
|
180
|
+
info "[dry-run] 将注入 git-instructions.md 到 <!-- teamkit-git:start/end --> 块"
|
|
181
|
+
return 0
|
|
182
|
+
fi
|
|
183
|
+
|
|
184
|
+
# 确保配置目录存在
|
|
185
|
+
mkdir -p "${OPENCODE_CONFIG_DIR}"
|
|
186
|
+
|
|
187
|
+
# 注入 KH instructions(<!-- knowledge-hub:start/end -->)
|
|
188
|
+
node "${MERGE_SCRIPT}" \
|
|
189
|
+
--target "${AGENTS_MD}" \
|
|
190
|
+
--template "${KH_TEMPLATE}"
|
|
191
|
+
|
|
192
|
+
# 注入 git instructions(<!-- teamkit-git:start/end -->)
|
|
193
|
+
# merge-agents-md.mjs 只处理 knowledge-hub marker,
|
|
194
|
+
# git-instructions 需要单独处理 teamkit-git marker
|
|
195
|
+
# 通过 argv 传路径,heredoc 用单引号防止变量展开
|
|
196
|
+
node - "${AGENTS_MD}" "${GIT_TEMPLATE}" <<'NODEJS_EOF'
|
|
197
|
+
const fs = require('fs');
|
|
198
|
+
const agentsMdPath = process.argv[2];
|
|
199
|
+
const gitTemplatePath = process.argv[3];
|
|
200
|
+
const template = fs.readFileSync(gitTemplatePath, 'utf8').replace(/\n+$/, '');
|
|
201
|
+
const GIT_START = '<!-- teamkit-git:start -->';
|
|
202
|
+
const GIT_END = '<!-- teamkit-git:end -->';
|
|
203
|
+
|
|
204
|
+
let content = fs.existsSync(agentsMdPath) ? fs.readFileSync(agentsMdPath, 'utf8') : '';
|
|
205
|
+
|
|
206
|
+
const startIdx = content.indexOf(GIT_START);
|
|
207
|
+
const endIdx = content.indexOf(GIT_END);
|
|
208
|
+
|
|
209
|
+
if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
|
|
210
|
+
const before = content.slice(0, startIdx + GIT_START.length);
|
|
211
|
+
const after = content.slice(endIdx);
|
|
212
|
+
content = before + '\n\n' + template + '\n\n' + after;
|
|
213
|
+
process.stdout.write(' ✓ AGENTS.md teamkit-git 块已更新\n');
|
|
214
|
+
} else {
|
|
215
|
+
content = content + '\n\n' + GIT_START + '\n\n' + template + '\n\n' + GIT_END + '\n';
|
|
216
|
+
process.stdout.write(' ✓ AGENTS.md teamkit-git 块已追加\n');
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
fs.writeFileSync(agentsMdPath, content, 'utf8');
|
|
220
|
+
NODEJS_EOF
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
inject_agents_md
|
|
224
|
+
|
|
225
|
+
# ────────────────────────────────────────────────────────────────────────────
|
|
226
|
+
# STEP 3:注册 Git Agent(teamkit-git subagent)到全局 agents 目录
|
|
227
|
+
# ────────────────────────────────────────────────────────────────────────────
|
|
228
|
+
info "Step 3/3: 注册 Git Agent 到 opencode agents 目录..."
|
|
229
|
+
|
|
230
|
+
register_git_agent() {
|
|
231
|
+
local agents_dir="${HOME}/.config/opencode/agents"
|
|
232
|
+
local src="${SCRIPT_DIR}/agents/teamkit-git.md"
|
|
233
|
+
|
|
234
|
+
if [[ ! -f "${src}" ]]; then
|
|
235
|
+
warn "agents/teamkit-git.md 不存在,跳过 Git Agent 注册"
|
|
236
|
+
return 0
|
|
237
|
+
fi
|
|
238
|
+
|
|
239
|
+
if [[ "${DRY_RUN}" == "1" ]]; then
|
|
240
|
+
info "[dry-run] 将复制 ${src} → ${agents_dir}/teamkit-git.md"
|
|
241
|
+
return 0
|
|
242
|
+
fi
|
|
243
|
+
|
|
244
|
+
mkdir -p "${agents_dir}"
|
|
245
|
+
cp "${src}" "${agents_dir}/teamkit-git.md"
|
|
246
|
+
echo " → Git Agent 已注册到 ${agents_dir}/teamkit-git.md"
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
register_git_agent
|
|
250
|
+
|
|
251
|
+
# ────────────────────────────────────────────────────────────────────────────
|
|
252
|
+
# STEP 4:初始化全局 git-rules.json(不存在时生成默认配置)
|
|
253
|
+
# 全局配置路径:~/.config/teamkit/git-rules.json
|
|
254
|
+
# 默认 narrative + zh,面向非程序员(程序员可在项目级覆盖)
|
|
255
|
+
# ────────────────────────────────────────────────────────────────────────────
|
|
256
|
+
info "Step 4/4: 初始化全局 git-rules.json..."
|
|
257
|
+
|
|
258
|
+
init_global_git_rules() {
|
|
259
|
+
local teamkit_config_dir="${HOME}/.config/teamkit"
|
|
260
|
+
local global_rules_file="${teamkit_config_dir}/git-rules.json"
|
|
261
|
+
|
|
262
|
+
if [[ "${DRY_RUN}" == "1" ]]; then
|
|
263
|
+
if [[ -f "${global_rules_file}" ]]; then
|
|
264
|
+
info "[dry-run] 全局 git-rules.json 已存在,将跳过(不覆盖): ${global_rules_file}"
|
|
265
|
+
else
|
|
266
|
+
info "[dry-run] 将创建全局 git-rules.json: ${global_rules_file}"
|
|
267
|
+
fi
|
|
268
|
+
return 0
|
|
269
|
+
fi
|
|
270
|
+
|
|
271
|
+
if [[ -f "${global_rules_file}" ]]; then
|
|
272
|
+
ok "全局 git-rules.json 已存在,跳过(保留用户配置)"
|
|
273
|
+
return 0
|
|
274
|
+
fi
|
|
275
|
+
|
|
276
|
+
mkdir -p "${teamkit_config_dir}"
|
|
277
|
+
cat > "${global_rules_file}" <<'JSON_EOF'
|
|
278
|
+
{
|
|
279
|
+
"branchPrefixes": ["feat/", "fix/", "docs/", "art/", "design/", "data/"],
|
|
280
|
+
"messageStyle": "narrative",
|
|
281
|
+
"messageLanguage": "zh",
|
|
282
|
+
"maxFilesPerCommit": 20,
|
|
283
|
+
"sensitivePatterns": [],
|
|
284
|
+
"lfsThresholdMB": 10
|
|
285
|
+
}
|
|
286
|
+
JSON_EOF
|
|
287
|
+
ok "已生成全局 git-rules.json(narrative + zh,适合非程序员): ${global_rules_file}"
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
init_global_git_rules
|
|
291
|
+
|
|
292
|
+
# ────────────────────────────────────────────────────────────────────────────
|
|
293
|
+
# 结尾输出
|
|
294
|
+
# ────────────────────────────────────────────────────────────────────────────
|
|
295
|
+
if [[ "${DRY_RUN}" == "1" ]]; then
|
|
296
|
+
info "[dry-run] 以上为模拟操作,未实际写文件"
|
|
297
|
+
printf "\n"
|
|
298
|
+
ok "dry-run 完成。使用不带 --dry-run 参数运行以实际安装。"
|
|
299
|
+
else
|
|
300
|
+
printf "\n"
|
|
301
|
+
ok "teamkit installed successfully. Run: teamkit doctor to verify."
|
|
302
|
+
fi
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@andyqiu/teamkit",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Team-level shared OpenCode plugin: KH integration + future team-wide workflows",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.cjs",
|
|
7
|
+
"bin": {
|
|
8
|
+
"teamkit": "bin/teamkit.mjs",
|
|
9
|
+
"teamkit-doctor": "bin/teamkit-doctor.mjs"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist/index.cjs",
|
|
13
|
+
"bin/",
|
|
14
|
+
"agents/",
|
|
15
|
+
"context-templates/",
|
|
16
|
+
"scripts/",
|
|
17
|
+
"install.sh",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "bun build src/index.ts --outfile dist/index.cjs --target=node --format=cjs",
|
|
23
|
+
"test": "node tests/compaction-guard.test.ts",
|
|
24
|
+
"test:verify": "echo 'TODO: vitest --run tests/'",
|
|
25
|
+
"typecheck": "tsc --noEmit",
|
|
26
|
+
"lint": "echo 'no lint configured (pre-publish TODO)'",
|
|
27
|
+
"prepublishOnly": "npm run build && npm run typecheck && npm test",
|
|
28
|
+
"adr:check": "node scripts/adr-check.mjs",
|
|
29
|
+
"adr:sync": "node scripts/adr-index-sync.mjs",
|
|
30
|
+
"adr:check-sync": "node scripts/adr-index-sync.mjs --check",
|
|
31
|
+
"publish:patch": "bash scripts/publish.sh --patch",
|
|
32
|
+
"publish:minor": "bash scripts/publish.sh --minor",
|
|
33
|
+
"publish:major": "bash scripts/publish.sh --major",
|
|
34
|
+
"publish:release": "bash scripts/publish.sh"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/node": "^25.9.1",
|
|
38
|
+
"typescript": "^5.0.0"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"@opencode-ai/plugin": "*"
|
|
42
|
+
},
|
|
43
|
+
"author": "Andy",
|
|
44
|
+
"license": "MIT"
|
|
45
|
+
}
|