@andyqiu/codeforge 0.5.29 → 0.6.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/agents/codeforge.md +1 -1
- package/agents/coder-deep.md +1 -1
- package/agents/coder-quick.md +1 -1
- package/agents/coder.md +1 -1
- package/agents/discover-challenger.md +0 -1
- package/agents/discover.md +0 -1
- package/agents/planner.md +1 -1
- package/agents/reviewer-lite.md +1 -1
- package/agents/reviewer.md +1 -1
- package/bin/codeforge.mjs +22 -48
- package/dist/index.js +604 -2074
- package/install.mjs +754 -0
- package/package.json +5 -7
- package/context-templates/kh-instructions.md +0 -109
- package/install.ps1 +0 -559
- package/install.sh +0 -726
- package/scripts/merge-agents-md.mjs +0 -228
package/install.sh
DELETED
|
@@ -1,726 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# ============================================================================
|
|
3
|
-
# CodeForge install.sh — 零侵入安装到 opencode (v0.1+ 单 bundle 架构)
|
|
4
|
-
#
|
|
5
|
-
# CodeForge 改为 OMO 风格的单 plugin bundle 架构:
|
|
6
|
-
# 1. 17 个能力被 bun build 编译成 dist/index.js 一个 ESM bundle
|
|
7
|
-
# 2. 在 opencode.json 里只占 1 行 plugin entry
|
|
8
|
-
# 3. 永久避免 opencode 1.14+ 早期版本的 zod 跨实例 bug(issue #12336/#21155)
|
|
9
|
-
#
|
|
10
|
-
# 行为:
|
|
11
|
-
# 1. 检测 opencode CLI / KH MCP
|
|
12
|
-
# 2. 构建 dist/index.js(除非 --skip-build)
|
|
13
|
-
# 3. 把 dist/index.js 复制到 ~/.config/opencode/codeforge/index.js
|
|
14
|
-
# 4. 在 ~/.config/opencode/opencode.json 的 "plugin" 数组追加 file:// URL
|
|
15
|
-
# 5. 把 agents/commands 用 file-by-file copy 注入(带白名单)
|
|
16
|
-
# 6. 把 workflows/context-templates/review-profiles 整目录拷贝
|
|
17
|
-
# 7. 智能合并 AGENTS.md:
|
|
18
|
-
# - 项目模式 + 已有 AGENTS.md → 替换 <!-- knowledge-hub:start -->...<!-- knowledge-hub:end --> 块(带 .bak 备份)
|
|
19
|
-
# - 项目模式 + 无 AGENTS.md → 生成含 marker 块的短版骨架
|
|
20
|
-
# - 全局模式 → 跳过(context-templates 已经放进 ~/.config/opencode/context-templates/ 给所有项目复用)
|
|
21
|
-
# 8. --global 时生成 ~/.config/codeforge/kh.json 模板(不含 token,硬约束 #2)
|
|
22
|
-
# 9. 输出验证清单
|
|
23
|
-
#
|
|
24
|
-
# 用法:
|
|
25
|
-
# ./install.sh # 项目级(默认)
|
|
26
|
-
# ./install.sh --global # 全局
|
|
27
|
-
# ./install.sh --uninstall # 卸载
|
|
28
|
-
# ./install.sh --dry-run # 仅打印操作,不执行
|
|
29
|
-
# ./install.sh --skip-build # 跳过 npm run build
|
|
30
|
-
# ============================================================================
|
|
31
|
-
|
|
32
|
-
set -euo pipefail
|
|
33
|
-
|
|
34
|
-
# ────────────── 颜色 ──────────────
|
|
35
|
-
if [[ -t 1 ]]; then
|
|
36
|
-
C_RESET='\033[0m'; C_BOLD='\033[1m'
|
|
37
|
-
C_RED='\033[31m'; C_GREEN='\033[32m'; C_YELLOW='\033[33m'; C_BLUE='\033[34m'; C_CYAN='\033[36m'
|
|
38
|
-
else
|
|
39
|
-
C_RESET=''; C_BOLD=''; C_RED=''; C_GREEN=''; C_YELLOW=''; C_BLUE=''; C_CYAN=''
|
|
40
|
-
fi
|
|
41
|
-
|
|
42
|
-
log() { printf '%b\n' "${C_CYAN}[codeforge]${C_RESET} $*"; }
|
|
43
|
-
ok() { printf '%b\n' "${C_GREEN}✓${C_RESET} $*"; }
|
|
44
|
-
warn() { printf '%b\n' "${C_YELLOW}⚠${C_RESET} $*"; }
|
|
45
|
-
err() { printf '%b\n' "${C_RED}✗${C_RESET} $*" >&2; }
|
|
46
|
-
hr() { printf '%b\n' "${C_BOLD}────────────────────────────────────────────────${C_RESET}"; }
|
|
47
|
-
|
|
48
|
-
# ────────────── ADR-0050: WSL 路径错位拒绝(先于参数解析,用原始 $* 检测 --uninstall) ──────────────
|
|
49
|
-
# 防止在 WSL 中 $HOME 落在 /mnt/c|d|e/Users/ 导致 codeforge 配置写到
|
|
50
|
-
# Windows 文件系统(NTFS 性能差、权限模型不同、git/symlink 行为异常)。
|
|
51
|
-
#
|
|
52
|
-
# 触发条件:
|
|
53
|
-
# - 系统真存在 wslpath 命令(WSL 环境标志),或
|
|
54
|
-
# - 测试旁路 CODEFORGE_FORCE_WSL_CHECK=1(CI/单测中强制走分支)
|
|
55
|
-
#
|
|
56
|
-
# 用户逃生口:
|
|
57
|
-
# - CODEFORGE_ALLOW_WSL_WINDOWS_HOME=1 → 强制继续(不推荐)
|
|
58
|
-
# - --uninstall 参数 → guard 整段跳过(用户可能就是要从错位位置清理掉,
|
|
59
|
-
# ADR-0050 / reviewer Phase 3 #3:卸载场景必须能跑通)
|
|
60
|
-
#
|
|
61
|
-
# 这里用 `$*` 原始参数串做模式匹配,**不依赖**下方 while 循环解析出的 ACTION
|
|
62
|
-
# 变量,因为本段必须在参数解析之前执行(错位路径下连参数解析的 set -u 都可能
|
|
63
|
-
# 因为环境异常而炸,guard 必须最早跑)。
|
|
64
|
-
if [[ " $* " != *" --uninstall "* ]]; then
|
|
65
|
-
if command -v wslpath >/dev/null 2>&1 || [[ "${CODEFORGE_FORCE_WSL_CHECK:-}" == "1" ]]; then
|
|
66
|
-
case "$HOME" in
|
|
67
|
-
/mnt/c/*|/mnt/d/*|/mnt/e/*)
|
|
68
|
-
err "在 WSL 中检测到 \$HOME=$HOME 落在 Windows 挂载路径下"
|
|
69
|
-
err "这会导致 codeforge 配置写到 Windows 文件系统,引发权限 / 性能 / 路径转换问题"
|
|
70
|
-
err "请改用 WSL 原生 home(通常是 /home/<user>/),或在 /etc/wsl.conf 中显式设置 default=<linux-user>"
|
|
71
|
-
err "如确实需要继续,设置环境变量 CODEFORGE_ALLOW_WSL_WINDOWS_HOME=1 跳过检查(不推荐)"
|
|
72
|
-
err "如果你是想卸载之前装错位置的 codeforge,加 --uninstall 参数会跳过本检查"
|
|
73
|
-
if [[ "${CODEFORGE_ALLOW_WSL_WINDOWS_HOME:-}" != "1" ]]; then
|
|
74
|
-
exit 1
|
|
75
|
-
fi
|
|
76
|
-
warn "已通过 CODEFORGE_ALLOW_WSL_WINDOWS_HOME=1 跳过 WSL 路径检查(请确保你清楚后果)"
|
|
77
|
-
;;
|
|
78
|
-
esac
|
|
79
|
-
fi
|
|
80
|
-
fi
|
|
81
|
-
|
|
82
|
-
# ────────────── 参数 ──────────────
|
|
83
|
-
MODE="project"
|
|
84
|
-
ACTION="install"
|
|
85
|
-
DRY_RUN=0
|
|
86
|
-
SKIP_BUILD=0
|
|
87
|
-
|
|
88
|
-
while [[ $# -gt 0 ]]; do
|
|
89
|
-
case "$1" in
|
|
90
|
-
--global) MODE="global" ;;
|
|
91
|
-
--project) MODE="project" ;;
|
|
92
|
-
--uninstall) ACTION="uninstall" ;;
|
|
93
|
-
--dry-run) DRY_RUN=1 ;;
|
|
94
|
-
--skip-build) SKIP_BUILD=1 ;;
|
|
95
|
-
-h|--help)
|
|
96
|
-
sed -n '3,30p' "$0"
|
|
97
|
-
exit 0
|
|
98
|
-
;;
|
|
99
|
-
*) err "未知参数:$1"; exit 2 ;;
|
|
100
|
-
esac
|
|
101
|
-
shift
|
|
102
|
-
done
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
# ────────────── 路径解析 ──────────────
|
|
106
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
107
|
-
SOURCE_ROOT="$SCRIPT_DIR"
|
|
108
|
-
|
|
109
|
-
if [[ "$MODE" == "global" ]]; then
|
|
110
|
-
TARGET_ROOT="${XDG_CONFIG_HOME:-$HOME/.config}/opencode"
|
|
111
|
-
else
|
|
112
|
-
TARGET_ROOT="$(pwd)/.opencode"
|
|
113
|
-
fi
|
|
114
|
-
|
|
115
|
-
# CodeForge 自身的全局配置目录(与 opencode 配置目录同级)
|
|
116
|
-
CODEFORGE_CFG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/codeforge"
|
|
117
|
-
|
|
118
|
-
# v0.1 之前装的目录(卸载时一并清掉)
|
|
119
|
-
LEGACY_DIRS=(agent command tool tools plugin plugins lib)
|
|
120
|
-
# v0.1+ 才有的目录(review-profiles 由 ADR:reviewer-multi-profile 引入)
|
|
121
|
-
# agent-templates 由 ADR:agent-templates-move-to-root 移到根目录(supersedes agent-templates-distribution)
|
|
122
|
-
MANAGED_DIRS=(codeforge agents commands workflows context-templates review-profiles agent-templates)
|
|
123
|
-
|
|
124
|
-
BUNDLE_SRC_REL="dist/index.js"
|
|
125
|
-
BUNDLE_DST_REL="codeforge/index.js"
|
|
126
|
-
|
|
127
|
-
# 文件级 copy 白名单(.md,排除 README/_*/.bak/.*)
|
|
128
|
-
MD_COPY_DIRS=("agents:agents" "commands:commands")
|
|
129
|
-
# 整目录拷贝(review-profiles 由 ADR:reviewer-multi-profile 引入,避开 maxdepth 1 限制)
|
|
130
|
-
# ADR:agent-templates-move-to-root — agent-templates 随 install 分发到全局目录
|
|
131
|
-
COPY_DIRS=("workflows:workflows" "context-templates:context-templates" "review-profiles:review-profiles" "agent-templates:agent-templates")
|
|
132
|
-
|
|
133
|
-
# KH 行为规范模板(B3 智能合并的输入)
|
|
134
|
-
KH_TEMPLATE_REL="context-templates/kh-instructions.md"
|
|
135
|
-
|
|
136
|
-
# ────────────── 工具函数 ──────────────
|
|
137
|
-
run() {
|
|
138
|
-
if [[ $DRY_RUN -eq 1 ]]; then
|
|
139
|
-
printf " ${C_BLUE}[dry-run]${C_RESET} %s\n" "$*"
|
|
140
|
-
else
|
|
141
|
-
eval "$@"
|
|
142
|
-
fi
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
ensure_dir() {
|
|
146
|
-
if [[ ! -d "$1" ]]; then
|
|
147
|
-
run "mkdir -p '$1'"
|
|
148
|
-
fi
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
# 计算 plugin 入口的 file:// URL
|
|
152
|
-
plugin_uri() {
|
|
153
|
-
local abs="$TARGET_ROOT/$BUNDLE_DST_REL"
|
|
154
|
-
echo "file://$abs"
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
opencode_cfg_path() {
|
|
158
|
-
echo "$TARGET_ROOT/opencode.json"
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
# 配置默认 agent → codeforge(ADR-0056 D5 / D2 实施细节)
|
|
162
|
-
#
|
|
163
|
-
# 在 ~/.config/opencode/opencode.json 顶层幂等合并 "default_agent": "codeforge"。
|
|
164
|
-
# 行为规则:
|
|
165
|
-
# - 字段不存在 → 写入 codeforge
|
|
166
|
-
# - 字段已是 codeforge → unchanged
|
|
167
|
-
# - 字段为其他值(用户自配) → 跳过不动 + 打 warn(绝不覆盖用户配置)
|
|
168
|
-
# 仅在 MODE=user/global 调用(项目级配置由用户自己管,不动)。
|
|
169
|
-
# 失败不阻塞 install 主流程(warn + return 0)。
|
|
170
|
-
# 用 node 处理 JSON(避免 sed/awk 处理 JSON 转义边界)。
|
|
171
|
-
configure_default_agent() {
|
|
172
|
-
local opencode_json="${XDG_CONFIG_HOME:-$HOME/.config}/opencode/opencode.json"
|
|
173
|
-
local target_agent="codeforge"
|
|
174
|
-
|
|
175
|
-
if [[ ! -f "$opencode_json" ]]; then
|
|
176
|
-
warn "opencode.json 不存在,跳过 default_agent 配置(首次安装 opencode 后请手动重跑 codeforge install)"
|
|
177
|
-
return 0
|
|
178
|
-
fi
|
|
179
|
-
|
|
180
|
-
if ! command -v node >/dev/null 2>&1; then
|
|
181
|
-
warn "未找到 node,跳过 default_agent 配置(请安装 Node.js >= 20 后重跑 install)"
|
|
182
|
-
return 0
|
|
183
|
-
fi
|
|
184
|
-
|
|
185
|
-
if [[ $DRY_RUN -eq 1 ]]; then
|
|
186
|
-
printf " ${C_BLUE}[dry-run]${C_RESET} configure default_agent=%s in %s\n" "$target_agent" "$opencode_json"
|
|
187
|
-
return 0
|
|
188
|
-
fi
|
|
189
|
-
|
|
190
|
-
local result
|
|
191
|
-
result=$(CFG="$opencode_json" TARGET="$target_agent" node -e '
|
|
192
|
-
const fs = require("node:fs");
|
|
193
|
-
const path = process.env.CFG;
|
|
194
|
-
const target = process.env.TARGET;
|
|
195
|
-
let cfg;
|
|
196
|
-
try { cfg = JSON.parse(fs.readFileSync(path, "utf8")); }
|
|
197
|
-
catch (e) { console.log("ERROR: " + e.message); process.exit(0); }
|
|
198
|
-
const current = cfg.default_agent;
|
|
199
|
-
if (current === undefined) {
|
|
200
|
-
cfg.default_agent = target;
|
|
201
|
-
fs.writeFileSync(path, JSON.stringify(cfg, null, 2) + "\n", "utf8");
|
|
202
|
-
console.log("SET default_agent=" + target);
|
|
203
|
-
} else if (current === target) {
|
|
204
|
-
console.log("UNCHANGED (already " + target + ")");
|
|
205
|
-
} else {
|
|
206
|
-
console.log("SKIPPED: user has default_agent=\"" + current + "\", not touching");
|
|
207
|
-
}
|
|
208
|
-
' 2>&1) || {
|
|
209
|
-
warn "default_agent 配置失败(不阻塞 install 主流程)"
|
|
210
|
-
return 0
|
|
211
|
-
}
|
|
212
|
-
ok "default_agent: $result"
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
# 卸载时还原 default_agent(仅当当前值是 codeforge 时移除字段)。
|
|
216
|
-
# 用户改成其他值的不动。
|
|
217
|
-
restore_default_agent() {
|
|
218
|
-
local opencode_json="${XDG_CONFIG_HOME:-$HOME/.config}/opencode/opencode.json"
|
|
219
|
-
if [[ ! -f "$opencode_json" ]]; then
|
|
220
|
-
return 0
|
|
221
|
-
fi
|
|
222
|
-
if ! command -v node >/dev/null 2>&1; then
|
|
223
|
-
warn "未找到 node,跳过 default_agent 卸载还原"
|
|
224
|
-
return 0
|
|
225
|
-
fi
|
|
226
|
-
if [[ $DRY_RUN -eq 1 ]]; then
|
|
227
|
-
printf " ${C_BLUE}[dry-run]${C_RESET} restore default_agent in %s (remove if codeforge)\n" "$opencode_json"
|
|
228
|
-
return 0
|
|
229
|
-
fi
|
|
230
|
-
|
|
231
|
-
local result
|
|
232
|
-
result=$(CFG="$opencode_json" node -e '
|
|
233
|
-
const fs = require("node:fs");
|
|
234
|
-
const path = process.env.CFG;
|
|
235
|
-
let cfg;
|
|
236
|
-
try { cfg = JSON.parse(fs.readFileSync(path, "utf8")); }
|
|
237
|
-
catch { process.exit(0); }
|
|
238
|
-
if (cfg.default_agent === "codeforge") {
|
|
239
|
-
delete cfg.default_agent;
|
|
240
|
-
fs.writeFileSync(path, JSON.stringify(cfg, null, 2) + "\n", "utf8");
|
|
241
|
-
console.log("REMOVED default_agent (was codeforge)");
|
|
242
|
-
} else if (cfg.default_agent !== undefined) {
|
|
243
|
-
console.log("SKIPPED: default_agent=\"" + cfg.default_agent + "\" not set by codeforge");
|
|
244
|
-
} else {
|
|
245
|
-
console.log("NOOP (no default_agent)");
|
|
246
|
-
}
|
|
247
|
-
' 2>&1) || return 0
|
|
248
|
-
ok "default_agent: $result"
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
# 写 ~/.config/codeforge/kh.json 模板(仅 --global 调用)
|
|
252
|
-
#
|
|
253
|
-
# 硬约束 #2:API key 绝不能落盘,模板里**不写** token / apiKey 字段。
|
|
254
|
-
# 用户必须通过环境变量 KNOWLEDGE_API_KEY 提供。
|
|
255
|
-
# 已存在则跳过,避免覆盖用户自定义。
|
|
256
|
-
write_kh_template() {
|
|
257
|
-
ensure_dir "$CODEFORGE_CFG_DIR"
|
|
258
|
-
local kh_file="$CODEFORGE_CFG_DIR/kh.json"
|
|
259
|
-
if [[ -f "$kh_file" ]]; then
|
|
260
|
-
ok "已存在 ${kh_file},跳过覆盖"
|
|
261
|
-
return 0
|
|
262
|
-
fi
|
|
263
|
-
if [[ $DRY_RUN -eq 1 ]]; then
|
|
264
|
-
printf " ${C_BLUE}[dry-run]${C_RESET} write %s (no token)\n" "$kh_file"
|
|
265
|
-
return 0
|
|
266
|
-
fi
|
|
267
|
-
cat > "$kh_file" <<'EOF'
|
|
268
|
-
{
|
|
269
|
-
"url": "http://10.5.60.26:8900/mcp",
|
|
270
|
-
"timeoutMs": 5000,
|
|
271
|
-
"maxRetries": 1
|
|
272
|
-
}
|
|
273
|
-
EOF
|
|
274
|
-
ok "已生成 ${kh_file}(API key 请通过环境变量 KNOWLEDGE_API_KEY 提供,禁止写入文件)"
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
# 智能合并项目根 AGENTS.md:
|
|
278
|
-
# - 已有 AGENTS.md → 替换 <!-- knowledge-hub:start -->...<!-- knowledge-hub:end --> 块(带 .bak 备份)
|
|
279
|
-
# - 不存在 AGENTS.md → 生成含 marker 块的短版骨架
|
|
280
|
-
#
|
|
281
|
-
# 实现委托给 scripts/merge-agents-md.mjs CLI(C.4 单一入口),
|
|
282
|
-
# 算法源自 lib/agents-merge.ts,sh/ps1 共用同一份 Node 实现,
|
|
283
|
-
# 避免 install 脚本各自维护算法副本造成漂移。
|
|
284
|
-
merge_project_agents_md() {
|
|
285
|
-
local agents_target="$1"
|
|
286
|
-
local template_path="$2"
|
|
287
|
-
local cli_path="$SOURCE_ROOT/scripts/merge-agents-md.mjs"
|
|
288
|
-
if [[ ! -f "$template_path" ]]; then
|
|
289
|
-
warn "模板不存在,跳过 AGENTS.md 合并: $template_path"
|
|
290
|
-
return 0
|
|
291
|
-
fi
|
|
292
|
-
if [[ ! -f "$cli_path" ]]; then
|
|
293
|
-
warn "merge CLI 不存在,跳过 AGENTS.md 智能合并: $cli_path"
|
|
294
|
-
return 0
|
|
295
|
-
fi
|
|
296
|
-
if ! command -v node >/dev/null 2>&1; then
|
|
297
|
-
warn "未找到 node,跳过 AGENTS.md 智能合并(请安装 Node.js >= 20)"
|
|
298
|
-
return 0
|
|
299
|
-
fi
|
|
300
|
-
local cli_args=(--target "$agents_target" --template "$template_path")
|
|
301
|
-
if [[ $DRY_RUN -eq 1 ]]; then
|
|
302
|
-
cli_args+=(--dry-run)
|
|
303
|
-
fi
|
|
304
|
-
node "$cli_path" "${cli_args[@]}" || {
|
|
305
|
-
warn "AGENTS.md 合并失败(不阻塞 install 主流程)"
|
|
306
|
-
return 0
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
# 用 node 重写 opencode.json 的 plugin 数组(追加自己 + 去掉所有 codeforge legacy entry)
|
|
311
|
-
write_plugin_entry() {
|
|
312
|
-
local cfg; cfg="$(opencode_cfg_path)"
|
|
313
|
-
local uri; uri="$(plugin_uri)"
|
|
314
|
-
|
|
315
|
-
if ! command -v node >/dev/null 2>&1; then
|
|
316
|
-
err "需要 node 才能改写 opencode.json,请安装 Node.js >= 20"
|
|
317
|
-
return 1
|
|
318
|
-
fi
|
|
319
|
-
|
|
320
|
-
ensure_dir "$TARGET_ROOT"
|
|
321
|
-
|
|
322
|
-
if [[ $DRY_RUN -eq 1 ]]; then
|
|
323
|
-
printf " ${C_BLUE}[dry-run]${C_RESET} write %s plugin entry: %s\n" "$cfg" "$uri"
|
|
324
|
-
return 0
|
|
325
|
-
fi
|
|
326
|
-
|
|
327
|
-
CFG="$cfg" URI="$uri" node -e '
|
|
328
|
-
const fs = require("node:fs");
|
|
329
|
-
const path = process.env.CFG;
|
|
330
|
-
const uri = process.env.URI;
|
|
331
|
-
let cfg = {};
|
|
332
|
-
if (fs.existsSync(path)) {
|
|
333
|
-
try { cfg = JSON.parse(fs.readFileSync(path, "utf8")); }
|
|
334
|
-
catch { fs.copyFileSync(path, path + ".bak." + Date.now()); cfg = {}; }
|
|
335
|
-
}
|
|
336
|
-
if (!cfg.$schema) cfg.$schema = "https://opencode.ai/config.json";
|
|
337
|
-
if (!Array.isArray(cfg.plugin)) cfg.plugin = [];
|
|
338
|
-
const cleaned = [];
|
|
339
|
-
for (const e of cfg.plugin) {
|
|
340
|
-
const s = String(e);
|
|
341
|
-
if (/\/codeforge\/index\.js$/.test(s)) continue;
|
|
342
|
-
if (/\/plugins\/[^/]+\.ts$/.test(s) && /opencode/.test(s)) continue;
|
|
343
|
-
if (/\/\.opencode\/plugins\//.test(s)) continue;
|
|
344
|
-
cleaned.push(e);
|
|
345
|
-
}
|
|
346
|
-
cleaned.push(uri);
|
|
347
|
-
cfg.plugin = cleaned;
|
|
348
|
-
fs.writeFileSync(path, JSON.stringify(cfg, null, 2) + "\n", "utf8");
|
|
349
|
-
'
|
|
350
|
-
ok "opencode.json 已写入 plugin entry: $uri"
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
remove_plugin_entry() {
|
|
354
|
-
local cfg; cfg="$(opencode_cfg_path)"
|
|
355
|
-
if [[ ! -f "$cfg" ]]; then return 0; fi
|
|
356
|
-
if ! command -v node >/dev/null 2>&1; then
|
|
357
|
-
warn "未找到 node,跳过 opencode.json plugin entry 清理"
|
|
358
|
-
return 0
|
|
359
|
-
fi
|
|
360
|
-
|
|
361
|
-
if [[ $DRY_RUN -eq 1 ]]; then
|
|
362
|
-
printf " ${C_BLUE}[dry-run]${C_RESET} rewrite %s without codeforge plugin entry\n" "$cfg"
|
|
363
|
-
return 0
|
|
364
|
-
fi
|
|
365
|
-
|
|
366
|
-
CFG="$cfg" node -e '
|
|
367
|
-
const fs = require("node:fs");
|
|
368
|
-
const path = process.env.CFG;
|
|
369
|
-
let cfg;
|
|
370
|
-
try { cfg = JSON.parse(fs.readFileSync(path, "utf8")); }
|
|
371
|
-
catch { return; }
|
|
372
|
-
if (!Array.isArray(cfg.plugin)) return;
|
|
373
|
-
cfg.plugin = cfg.plugin.filter(e => {
|
|
374
|
-
const s = String(e);
|
|
375
|
-
if (/\/codeforge\/index\.js$/.test(s)) return false;
|
|
376
|
-
if (/\/plugins\/[^/]+\.ts$/.test(s)) return false;
|
|
377
|
-
if (/\/\.opencode\/plugins\//.test(s)) return false;
|
|
378
|
-
return true;
|
|
379
|
-
});
|
|
380
|
-
fs.writeFileSync(path, JSON.stringify(cfg, null, 2) + "\n", "utf8");
|
|
381
|
-
'
|
|
382
|
-
ok "opencode.json 已移除 codeforge plugin entry"
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
# 卸载
|
|
386
|
-
uninstall() {
|
|
387
|
-
log "卸载 CodeForge from: $TARGET_ROOT"
|
|
388
|
-
remove_plugin_entry
|
|
389
|
-
# 还原 default_agent(仅 user/global mode;project mode 不动用户级配置)
|
|
390
|
-
if [[ "$MODE" != "project" ]]; then
|
|
391
|
-
restore_default_agent
|
|
392
|
-
fi
|
|
393
|
-
for name in "${LEGACY_DIRS[@]}" "${MANAGED_DIRS[@]}"; do
|
|
394
|
-
local p="$TARGET_ROOT/$name"
|
|
395
|
-
if [[ -e "$p" || -L "$p" ]]; then
|
|
396
|
-
run "rm -rf '$p'"
|
|
397
|
-
ok "已删除 $p"
|
|
398
|
-
fi
|
|
399
|
-
done
|
|
400
|
-
hr
|
|
401
|
-
# 细粒度删除 skills:只删 CodeForge 自己的 skill,不删用户自装的(如 frontend-design)
|
|
402
|
-
OWNED_SKILLS=(ambiguity-gate devils-advocate ears-zh example-mapping success-criteria weighted-dimensions)
|
|
403
|
-
for skill_name in "${OWNED_SKILLS[@]}"; do
|
|
404
|
-
local p="$TARGET_ROOT/skills/$skill_name"
|
|
405
|
-
if [[ -e "$p" || -L "$p" ]]; then
|
|
406
|
-
run "rm -rf '$p'"
|
|
407
|
-
ok "已删除 skill: $p"
|
|
408
|
-
fi
|
|
409
|
-
done
|
|
410
|
-
ok "卸载完成(opencode 自身和你的 AGENTS.md / ~/.config/codeforge/kh.json 不会被动)"
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
# 检测
|
|
414
|
-
detect_opencode() {
|
|
415
|
-
if command -v opencode >/dev/null 2>&1; then
|
|
416
|
-
local v; v="$(opencode --version 2>/dev/null || echo "unknown")"
|
|
417
|
-
ok "检测到 opencode: $v"
|
|
418
|
-
else
|
|
419
|
-
warn "未检测到 opencode CLI"
|
|
420
|
-
warn " 安装方式:https://github.com/sst/opencode#installation"
|
|
421
|
-
fi
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
detect_kh_mcp() {
|
|
425
|
-
local cfg="${XDG_CONFIG_HOME:-$HOME/.config}/opencode/opencode.json"
|
|
426
|
-
if [[ -f "$cfg" ]] && grep -q "knowledge-hub\|code-forge-knowledge-hub" "$cfg" 2>/dev/null; then
|
|
427
|
-
ok "检测到 Knowledge Hub MCP 已注册"
|
|
428
|
-
else
|
|
429
|
-
warn "未在 $cfg 中找到 Knowledge Hub MCP"
|
|
430
|
-
warn " 配置示例见 docs/PRD.md §6.2"
|
|
431
|
-
fi
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
# ────────────── 主流程 ──────────────
|
|
435
|
-
hr
|
|
436
|
-
log "${C_BOLD}CodeForge installer${C_RESET} (mode=$MODE, action=$ACTION, dry-run=$DRY_RUN)"
|
|
437
|
-
hr
|
|
438
|
-
log "Source : $SOURCE_ROOT"
|
|
439
|
-
log "Target : $TARGET_ROOT"
|
|
440
|
-
hr
|
|
441
|
-
|
|
442
|
-
if [[ "$ACTION" == "uninstall" ]]; then
|
|
443
|
-
uninstall
|
|
444
|
-
exit 0
|
|
445
|
-
fi
|
|
446
|
-
|
|
447
|
-
# Step 1/8: 环境检测
|
|
448
|
-
log "Step 1/8: 环境检测"
|
|
449
|
-
detect_opencode
|
|
450
|
-
detect_kh_mcp
|
|
451
|
-
|
|
452
|
-
# Step 2/8: build dist bundle
|
|
453
|
-
log "Step 2/8: 构建 dist/index.js 单 bundle"
|
|
454
|
-
BUNDLE_SRC="$SOURCE_ROOT/$BUNDLE_SRC_REL"
|
|
455
|
-
if [[ $SKIP_BUILD -eq 1 ]]; then
|
|
456
|
-
warn "已跳过 build(--skip-build),使用现有 dist/index.js"
|
|
457
|
-
else
|
|
458
|
-
if [[ $DRY_RUN -eq 1 ]]; then
|
|
459
|
-
printf " ${C_BLUE}[dry-run]${C_RESET} npm run build\n"
|
|
460
|
-
else
|
|
461
|
-
(cd "$SOURCE_ROOT" && npm run build) || { err "npm run build 失败"; exit 1; }
|
|
462
|
-
fi
|
|
463
|
-
fi
|
|
464
|
-
if [[ ! -f "$BUNDLE_SRC" && $DRY_RUN -eq 0 ]]; then
|
|
465
|
-
if [[ $SKIP_BUILD -eq 1 ]]; then
|
|
466
|
-
err "找不到 ${BUNDLE_SRC}(npm 包可能损坏)"
|
|
467
|
-
err " 请尝试重装:npx @andyqiu/codeforge install [--global]"
|
|
468
|
-
else
|
|
469
|
-
err "找不到 ${BUNDLE_SRC},请先成功执行 npm run build"
|
|
470
|
-
fi
|
|
471
|
-
exit 1
|
|
472
|
-
fi
|
|
473
|
-
if [[ -f "$BUNDLE_SRC" ]]; then
|
|
474
|
-
size=$(wc -c < "$BUNDLE_SRC" | tr -d ' ')
|
|
475
|
-
ok "bundle 已就绪: $BUNDLE_SRC (${size} bytes)"
|
|
476
|
-
fi
|
|
477
|
-
|
|
478
|
-
# Step 3/8: 准备目录 + 清理 legacy
|
|
479
|
-
log "Step 3/8: 准备目标目录 + 清理 legacy 注入物"
|
|
480
|
-
ensure_dir "$TARGET_ROOT"
|
|
481
|
-
for legacy in "${LEGACY_DIRS[@]}"; do
|
|
482
|
-
p="$TARGET_ROOT/$legacy"
|
|
483
|
-
if [[ -e "$p" || -L "$p" ]]; then
|
|
484
|
-
run "rm -rf '$p'"
|
|
485
|
-
warn "已清理 legacy 目录: $p"
|
|
486
|
-
fi
|
|
487
|
-
done
|
|
488
|
-
|
|
489
|
-
# Step 4/8: 装 bundle
|
|
490
|
-
log "Step 4/8: 装入 dist/index.js bundle"
|
|
491
|
-
BUNDLE_DST="$TARGET_ROOT/$BUNDLE_DST_REL"
|
|
492
|
-
ensure_dir "$(dirname "$BUNDLE_DST")"
|
|
493
|
-
run "cp -f '$BUNDLE_SRC' '$BUNDLE_DST'"
|
|
494
|
-
ok "bundle → $BUNDLE_DST"
|
|
495
|
-
write_plugin_entry
|
|
496
|
-
|
|
497
|
-
# 写 VERSION marker 文件(用户 cat 一行查版本,不依赖 grep bundle)
|
|
498
|
-
CF_VERSION="$(node -e "console.log(require('$SOURCE_ROOT/package.json').version)" 2>/dev/null || echo "unknown")"
|
|
499
|
-
VERSION_FILE="$TARGET_ROOT/codeforge/VERSION"
|
|
500
|
-
if [[ $DRY_RUN -eq 0 ]]; then
|
|
501
|
-
printf "%s\n" "$CF_VERSION" > "$VERSION_FILE"
|
|
502
|
-
fi
|
|
503
|
-
ok "VERSION → ${VERSION_FILE} (${CF_VERSION})"
|
|
504
|
-
|
|
505
|
-
# Step 5/8: 装 agents / commands / workflows / context-templates / review-profiles
|
|
506
|
-
log "Step 5/8: 装 agents / commands / workflows / context-templates / review-profiles"
|
|
507
|
-
for entry in "${MD_COPY_DIRS[@]}"; do
|
|
508
|
-
src_name="${entry%%:*}"; dst_name="${entry##*:}"
|
|
509
|
-
src_path="$SOURCE_ROOT/$src_name"
|
|
510
|
-
dst_path="$TARGET_ROOT/$dst_name"
|
|
511
|
-
if [[ ! -d "$src_path" ]]; then
|
|
512
|
-
warn "源目录不存在,跳过: $src_path"; continue
|
|
513
|
-
fi
|
|
514
|
-
if [[ -e "$dst_path" || -L "$dst_path" ]]; then
|
|
515
|
-
run "rm -rf '$dst_path'"
|
|
516
|
-
fi
|
|
517
|
-
ensure_dir "$dst_path"
|
|
518
|
-
count=0
|
|
519
|
-
while IFS= read -r f; do
|
|
520
|
-
base="$(basename "$f")"
|
|
521
|
-
if [[ "$base" == "README.md" || "$base" == _* || "$base" == *.bak || "$base" == .* ]]; then continue; fi
|
|
522
|
-
run "cp -f '$f' '$dst_path/'"
|
|
523
|
-
count=$((count+1))
|
|
524
|
-
done < <(find "$src_path" -maxdepth 1 -type f -name '*.md')
|
|
525
|
-
ok "$src_name/ → $dst_path ($count 个 .md)"
|
|
526
|
-
done
|
|
527
|
-
for entry in "${COPY_DIRS[@]}"; do
|
|
528
|
-
src_name="${entry%%:*}"; dst_name="${entry##*:}"
|
|
529
|
-
src_path="$SOURCE_ROOT/$src_name"
|
|
530
|
-
dst_path="$TARGET_ROOT/$dst_name"
|
|
531
|
-
if [[ ! -d "$src_path" ]]; then
|
|
532
|
-
warn "源目录不存在,跳过: $src_path"; continue
|
|
533
|
-
fi
|
|
534
|
-
ensure_dir "$dst_path"
|
|
535
|
-
run "cp -R '$src_path/.' '$dst_path/'"
|
|
536
|
-
ok "$src_name/ → $dst_path (整目录拷贝)"
|
|
537
|
-
done
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
# Step 5b/8: 装 skills/(opencode skill 目录:~/.config/opencode/skills/ 或 .opencode/skills/)
|
|
541
|
-
# skills/ 在项目顶级,git 追踪,npm 包含。安装时整目录 rsync 到 target。
|
|
542
|
-
# 细粒度 uninstall:只删 CodeForge 自己的 skill 子目录,不碰用户自装的其他 skill。
|
|
543
|
-
log "Step 5b/8: 装 skills/"
|
|
544
|
-
SKILLS_SRC="$SOURCE_ROOT/skills"
|
|
545
|
-
SKILLS_DST="$TARGET_ROOT/skills"
|
|
546
|
-
if [[ -d "$SKILLS_SRC" ]]; then
|
|
547
|
-
ensure_dir "$SKILLS_DST"
|
|
548
|
-
skill_count=0
|
|
549
|
-
while IFS= read -r skill_dir; do
|
|
550
|
-
skill_name="$(basename "$skill_dir")"
|
|
551
|
-
dst_skill="$SKILLS_DST/$skill_name"
|
|
552
|
-
if [[ -e "$dst_skill" || -L "$dst_skill" ]]; then
|
|
553
|
-
run "rm -rf '$dst_skill'"
|
|
554
|
-
fi
|
|
555
|
-
run "cp -R '$skill_dir' '$dst_skill'"
|
|
556
|
-
skill_count=$((skill_count + 1))
|
|
557
|
-
done < <(find "$SKILLS_SRC" -mindepth 1 -maxdepth 1 -type d)
|
|
558
|
-
ok "skills/ → $SKILLS_DST ($skill_count 个 skill)"
|
|
559
|
-
else
|
|
560
|
-
warn "skills/ 目录不存在,跳过(发布包未含 skills)"
|
|
561
|
-
fi
|
|
562
|
-
|
|
563
|
-
# Step 5c/8: 装 assets/(adr-init 模板资产:scripts/ + githooks/ + docs/)
|
|
564
|
-
# assets/ 在项目顶级,git 追踪,npm 包含。安装时整目录拷贝到 TARGET_ROOT/assets/。
|
|
565
|
-
# resolveAssetsRoot(tools/adr-init.ts)从 dist/index.js 所在目录向上找 package.json+assets/adr-init,
|
|
566
|
-
# 命中 ~/.config/opencode(层 1),因此 assets 必须装到 TARGET_ROOT/assets/adr-init/。
|
|
567
|
-
log "Step 5c/8: 装 assets/"
|
|
568
|
-
ASSETS_SRC="$SOURCE_ROOT/assets"
|
|
569
|
-
ASSETS_DST="$TARGET_ROOT/assets"
|
|
570
|
-
if [[ -d "$ASSETS_SRC" ]]; then
|
|
571
|
-
ensure_dir "$ASSETS_DST"
|
|
572
|
-
run "cp -R '$ASSETS_SRC/.' '$ASSETS_DST/'"
|
|
573
|
-
ok "assets/ → $ASSETS_DST"
|
|
574
|
-
else
|
|
575
|
-
warn "assets/ 目录不存在,跳过(发布包未含 assets)"
|
|
576
|
-
fi
|
|
577
|
-
|
|
578
|
-
# Step 6/8: AGENTS.md 智能合并(仅项目模式)
|
|
579
|
-
log "Step 6/8: AGENTS.md 智能合并"
|
|
580
|
-
if [[ "$MODE" == "project" ]]; then
|
|
581
|
-
agents_target="$(pwd)/AGENTS.md"
|
|
582
|
-
template_path="$SOURCE_ROOT/$KH_TEMPLATE_REL"
|
|
583
|
-
merge_project_agents_md "$agents_target" "$template_path"
|
|
584
|
-
else
|
|
585
|
-
ok "全局模式:跳过项目 AGENTS.md 合并(context-templates 已装到 $TARGET_ROOT/context-templates)"
|
|
586
|
-
fi
|
|
587
|
-
|
|
588
|
-
# Step 7/8: KH 全局配置模板(仅 --global)
|
|
589
|
-
log "Step 7/8: KH 全局配置模板"
|
|
590
|
-
if [[ "$MODE" == "global" ]]; then
|
|
591
|
-
write_kh_template
|
|
592
|
-
else
|
|
593
|
-
ok "项目级安装,跳过 KH 全局模板(要装请加 --global)"
|
|
594
|
-
fi
|
|
595
|
-
|
|
596
|
-
# Step 8/8: 配置默认 agent → codeforge(ADR-0056 D5)
|
|
597
|
-
# 仅 user/global mode 配置用户级 ~/.config/opencode/opencode.json
|
|
598
|
-
# project mode 不动用户级配置(项目应由用户自决是否覆盖)
|
|
599
|
-
# Windows 用户暂需手动配置(install.ps1 尚未实现该步骤,留待 ADR-0059)
|
|
600
|
-
log "Step 8/8: 配置默认 agent → codeforge"
|
|
601
|
-
if [[ "$MODE" == "global" ]]; then
|
|
602
|
-
configure_default_agent
|
|
603
|
-
else
|
|
604
|
-
ok "项目级安装,跳过用户级 default_agent 配置(要装请加 --global)"
|
|
605
|
-
fi
|
|
606
|
-
|
|
607
|
-
# 如果是 CodeForge 开发仓库本身,重新生成 dev shim(install 会清掉 .opencode/plugins/)
|
|
608
|
-
# ADR-0055:本仓库内 opencode 加载本项目 dist/ 而非全局 stable,依赖 dev shim 让位
|
|
609
|
-
if [ -f ".codeforge/.dev-marker" ] && [ -f "scripts/dev-sync.mjs" ]; then
|
|
610
|
-
node scripts/dev-sync.mjs --no-build 2>/dev/null || true
|
|
611
|
-
echo " ↳ dev shim regenerated (.opencode/plugins/codeforge-dev.js)"
|
|
612
|
-
fi
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
# Step 9/9: 安装后自检(ADR:worktree-guard-fail-loud Fix D)
|
|
616
|
-
# 在「验证清单」前主动跑 4 项关键依赖检查;warn 不阻塞 install,但用户能立刻看到红绿
|
|
617
|
-
verify_install() {
|
|
618
|
-
local has_warn=0
|
|
619
|
-
log "Step 9/9: 安装后自检"
|
|
620
|
-
|
|
621
|
-
# 1. git 版本(worktree 隔离硬依赖;需要 >= 2.5)
|
|
622
|
-
if command -v git >/dev/null 2>&1; then
|
|
623
|
-
local gv major minor
|
|
624
|
-
gv="$(git --version 2>/dev/null | awk '{print $3}')"
|
|
625
|
-
major="$(printf '%s' "$gv" | cut -d. -f1)"
|
|
626
|
-
minor="$(printf '%s' "$gv" | cut -d. -f2)"
|
|
627
|
-
# 数值校验失败时按 0 处理,避免 "[: : integer expression expected"
|
|
628
|
-
case "$major" in *[!0-9]*|"") major=0 ;; esac
|
|
629
|
-
case "$minor" in *[!0-9]*|"") minor=0 ;; esac
|
|
630
|
-
if [ "$major" -gt 2 ] || { [ "$major" -eq 2 ] && [ "$minor" -ge 5 ]; }; then
|
|
631
|
-
ok "git $gv (>= 2.5 ✓ worktree 支持)"
|
|
632
|
-
else
|
|
633
|
-
warn "git $gv 版本太旧(worktree 需要 >= 2.5);worktree 隔离将失效(写操作会被 DENY)"
|
|
634
|
-
has_warn=1
|
|
635
|
-
fi
|
|
636
|
-
else
|
|
637
|
-
warn "git 未安装;worktree 隔离将失效(写操作会被 DENY)"
|
|
638
|
-
has_warn=1
|
|
639
|
-
fi
|
|
640
|
-
|
|
641
|
-
# 1b. git 仓库探测(reviewer 追加:Fix D 补 git rev-parse --git-dir 检查)
|
|
642
|
-
# 全局模式下当前目录通常**不是** CodeForge 自己的项目,因此只 info 提示;
|
|
643
|
-
# 项目模式下若当前目录不是 git 仓库,必然影响 worktree 隔离 → warn。
|
|
644
|
-
if command -v git >/dev/null 2>&1; then
|
|
645
|
-
if git rev-parse --git-dir >/dev/null 2>&1; then
|
|
646
|
-
ok "当前目录是 git 仓库(git rev-parse --git-dir ✓)"
|
|
647
|
-
else
|
|
648
|
-
if [ "$MODE" = "project" ]; then
|
|
649
|
-
warn "当前目录不是 git 仓库(git rev-parse --git-dir 失败);worktree 隔离将失效"
|
|
650
|
-
warn " 解决:\`git init && git add -A && git commit -m 'initial'\`"
|
|
651
|
-
has_warn=1
|
|
652
|
-
else
|
|
653
|
-
log " ↳ 当前目录非 git 仓库(全局安装无碍;进入实际项目使用时该项目需是 git 仓库)"
|
|
654
|
-
fi
|
|
655
|
-
fi
|
|
656
|
-
fi
|
|
657
|
-
|
|
658
|
-
# 2. opencode 版本
|
|
659
|
-
if command -v opencode >/dev/null 2>&1; then
|
|
660
|
-
local ov
|
|
661
|
-
ov="$(opencode --version 2>/dev/null || echo unknown)"
|
|
662
|
-
ok "opencode $ov"
|
|
663
|
-
else
|
|
664
|
-
warn "opencode 未在 PATH 中;CodeForge 无入口可用(请先安装 opencode)"
|
|
665
|
-
has_warn=1
|
|
666
|
-
fi
|
|
667
|
-
|
|
668
|
-
# 3. plugin 注册检查
|
|
669
|
-
local cfg
|
|
670
|
-
cfg="$(opencode_cfg_path)"
|
|
671
|
-
if [ -f "$cfg" ] && grep -q '"codeforge"' "$cfg" 2>/dev/null; then
|
|
672
|
-
ok "plugin 已注册:$cfg"
|
|
673
|
-
else
|
|
674
|
-
if [ "$MODE" = "project" ]; then
|
|
675
|
-
log " ↳ 项目级配置 $cfg 未包含 codeforge entry(如需用 codeforge agent,请编辑该文件)"
|
|
676
|
-
else
|
|
677
|
-
warn "plugin 未在 $cfg 中注册"
|
|
678
|
-
has_warn=1
|
|
679
|
-
fi
|
|
680
|
-
fi
|
|
681
|
-
|
|
682
|
-
# 4. dev-marker 误用检测(仅 global 模式有意义)
|
|
683
|
-
if [ "$MODE" = "global" ]; then
|
|
684
|
-
if [ -f "$HOME/.codeforge/.dev-marker" ] || [ -f "$HOME/.dev-marker" ]; then
|
|
685
|
-
warn "检测到 \$HOME 附近有 .codeforge/.dev-marker —— 可能导致所有项目让位到本地 plugin"
|
|
686
|
-
warn " 建议删除:rm \$HOME/.codeforge/.dev-marker"
|
|
687
|
-
has_warn=1
|
|
688
|
-
fi
|
|
689
|
-
fi
|
|
690
|
-
|
|
691
|
-
if [ "$has_warn" -eq 0 ]; then
|
|
692
|
-
ok "${C_BOLD}自检通过${C_RESET}"
|
|
693
|
-
else
|
|
694
|
-
warn "${C_BOLD}自检发现 $has_warn 项问题,详见上方${C_RESET}"
|
|
695
|
-
fi
|
|
696
|
-
}
|
|
697
|
-
verify_install
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
# 验证清单
|
|
701
|
-
hr
|
|
702
|
-
ok "${C_BOLD}CodeForge v${CF_VERSION} 安装完成${C_RESET}"
|
|
703
|
-
hr
|
|
704
|
-
cat <<EOF
|
|
705
|
-
${C_BOLD}验证清单:${C_RESET}
|
|
706
|
-
1. 列出注入的扩展点:
|
|
707
|
-
$ ls -la "$TARGET_ROOT"
|
|
708
|
-
2. 看 opencode.json 里 plugin entry:
|
|
709
|
-
$ cat "$(opencode_cfg_path)"
|
|
710
|
-
3. 让 opencode 校验配置:
|
|
711
|
-
$ opencode debug config
|
|
712
|
-
-> 期望看到 1 个 codeforge plugin entry(不是 17 个)
|
|
713
|
-
4. 跑一次最小 dogfood,看 plugin import + activation 日志:
|
|
714
|
-
$ opencode run "hello"
|
|
715
|
-
$ tail -n 50 \$HOME/.cache/codeforge/plugins.log
|
|
716
|
-
5. KH 配置(仅 --global 安装时生成):
|
|
717
|
-
$ cat "$CODEFORGE_CFG_DIR/kh.json"
|
|
718
|
-
$ export KNOWLEDGE_API_KEY=<your-token> # ← API key 必须走环境变量
|
|
719
|
-
6. AGENTS.md 智能合并(仅项目级):
|
|
720
|
-
- 已有 AGENTS.md → KH 块被替换成模板内容,原文件备份成 *.bak.<timestamp>
|
|
721
|
-
- 模板源:${SOURCE_ROOT}/${KH_TEMPLATE_REL}(修改后请重新 install)
|
|
722
|
-
|
|
723
|
-
${C_BOLD}卸载:${C_RESET}
|
|
724
|
-
$ ./install.sh --uninstall ${MODE/project/}
|
|
725
|
-
EOF
|
|
726
|
-
hr
|