first
This commit is contained in:
Executable
+722
@@ -0,0 +1,722 @@
|
||||
#!/bin/bash
|
||||
# llm-wiki unified installer
|
||||
set -euo pipefail
|
||||
|
||||
SKILL_NAME="llm-wiki"
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
SOURCE_REGISTRY_SCRIPT="$SCRIPT_DIR/scripts/source-registry.sh"
|
||||
ADAPTER_STATE_SCRIPT="$SCRIPT_DIR/scripts/adapter-state.sh"
|
||||
PLATFORM="auto"
|
||||
PLATFORM_EXPLICIT=0
|
||||
DRY_RUN=0
|
||||
TARGET_DIR=""
|
||||
INSTALL_HOOKS=0
|
||||
UNINSTALL_HOOKS=0
|
||||
UPGRADE=0
|
||||
WITH_OPTIONAL_ADAPTERS=0
|
||||
|
||||
# 这些项目都在运行时会被读取或链接:
|
||||
# - 入口与说明文件:README / CLAUDE / AGENTS / CHANGELOG
|
||||
# - 安装入口:install.sh / setup.sh
|
||||
# - 实际执行内容:SKILL.md / scripts / templates / deps
|
||||
# - 平台薄入口:platforms(README、CLAUDE、AGENTS 都会引用)
|
||||
MANAGED_ITEMS=(
|
||||
"SKILL.md"
|
||||
"README.md"
|
||||
"CLAUDE.md"
|
||||
"AGENTS.md"
|
||||
"HERMES.md"
|
||||
"CHANGELOG.md"
|
||||
"install.sh"
|
||||
"setup.sh"
|
||||
"install.ps1"
|
||||
"scripts"
|
||||
"templates"
|
||||
"deps"
|
||||
"platforms"
|
||||
)
|
||||
|
||||
DEP_SKILLS=()
|
||||
|
||||
list_companion_skill_sources() {
|
||||
case "$1" in
|
||||
claude)
|
||||
printf '%s\n' "platforms/claude/companions/llm-wiki-upgrade"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 微信工具 URL 从共享配置读取,与 adapter-state.sh 保持一致
|
||||
source "$SCRIPT_DIR/scripts/shared-config.sh"
|
||||
source "$SCRIPT_DIR/scripts/runtime-context.sh"
|
||||
|
||||
info() { printf '\033[36m[信息]\033[0m %s\n' "$1"; }
|
||||
ok() { printf '\033[32m[完成]\033[0m %s\n' "$1"; }
|
||||
warn() { printf '\033[33m[警告]\033[0m %s\n' "$1"; }
|
||||
err() { printf '\033[31m[错误]\033[0m %s\n' "$1" >&2; }
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
用法:
|
||||
bash install.sh --platform <claude|codex|openclaw|hermes|auto> [--dry-run]
|
||||
bash install.sh --platform claude --install-hooks
|
||||
bash install.sh --install-hooks
|
||||
bash install.sh --uninstall-hooks
|
||||
bash install.sh --upgrade [--platform <claude|codex|openclaw|hermes|auto>]
|
||||
bash install.sh --platform codex --with-optional-adapters
|
||||
|
||||
选项:
|
||||
--platform 目标平台。默认 auto;只有检测到唯一平台时才会自动安装。
|
||||
--dry-run 只打印安装计划,不写入文件。
|
||||
--target-dir 指定技能目标目录(直接传最终的 llm-wiki 目录)。
|
||||
--with-optional-adapters 显式启用网页 / X / YouTube / 公众号等可选提取器安装。
|
||||
--install-hooks 注册 Claude Code 的 SessionStart hook。
|
||||
--uninstall-hooks 移除 Claude Code 的 SessionStart hook。
|
||||
--upgrade 拉取最新代码并更新已安装的 llm-wiki(保留 hook 配置)。
|
||||
-h, --help 显示帮助。
|
||||
EOF
|
||||
}
|
||||
|
||||
hook_command_for_skill_dir() {
|
||||
printf 'bash %s/scripts/hook-session-start.sh\n' "$1"
|
||||
}
|
||||
|
||||
require_jq() {
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
err "注册或移除 hook 需要 jq"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
register_claude_session_hook() {
|
||||
local skill_dir="$1"
|
||||
local settings_dir settings_path backup_path hook_command tmp_file
|
||||
|
||||
[ -d "$skill_dir" ] || {
|
||||
err "未找到已安装的 llm-wiki:$skill_dir"
|
||||
exit 1
|
||||
}
|
||||
|
||||
require_jq
|
||||
|
||||
settings_dir="$HOME/.claude"
|
||||
settings_path="$settings_dir/settings.json"
|
||||
backup_path="$settings_dir/settings.json.bak.llm-wiki"
|
||||
hook_command="$(hook_command_for_skill_dir "$skill_dir")"
|
||||
|
||||
if [ "$DRY_RUN" -eq 1 ]; then
|
||||
printf '[dry-run] register SessionStart hook: %s\n' "$hook_command"
|
||||
return 0
|
||||
fi
|
||||
|
||||
mkdir -p "$settings_dir"
|
||||
[ -f "$settings_path" ] || printf '{}\n' > "$settings_path"
|
||||
cp "$settings_path" "$backup_path"
|
||||
|
||||
if jq -e --arg cmd "$hook_command" '[ (.hooks.SessionStart // [])[]? | (.hooks // [])[]? | .command ] | index($cmd) != null' "$settings_path" > /dev/null; then
|
||||
ok "Claude Code SessionStart hook 已存在,跳过"
|
||||
return 0
|
||||
fi
|
||||
|
||||
tmp_file="$(mktemp)"
|
||||
jq --arg cmd "$hook_command" '
|
||||
.hooks = (.hooks // {}) |
|
||||
.hooks.SessionStart = ((.hooks.SessionStart // []) + [{"hooks":[{"type":"command","command":$cmd}]}])
|
||||
' "$settings_path" > "$tmp_file"
|
||||
mv "$tmp_file" "$settings_path"
|
||||
|
||||
ok "Claude Code SessionStart hook 已注册"
|
||||
}
|
||||
|
||||
uninstall_claude_session_hook() {
|
||||
local skill_dir="$1"
|
||||
local settings_dir settings_path backup_path hook_command tmp_file
|
||||
|
||||
require_jq
|
||||
|
||||
settings_dir="$HOME/.claude"
|
||||
settings_path="$settings_dir/settings.json"
|
||||
backup_path="$settings_dir/settings.json.bak.llm-wiki"
|
||||
hook_command="$(hook_command_for_skill_dir "$skill_dir")"
|
||||
|
||||
if [ ! -f "$settings_path" ]; then
|
||||
if [ "$DRY_RUN" -eq 1 ]; then
|
||||
printf '[dry-run] uninstall SessionStart hook: %s\n' "$hook_command"
|
||||
return 0
|
||||
fi
|
||||
ok "未找到 Claude Code settings.json,跳过 hook 移除"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ "$DRY_RUN" -eq 1 ]; then
|
||||
printf '[dry-run] uninstall SessionStart hook: %s\n' "$hook_command"
|
||||
return 0
|
||||
fi
|
||||
|
||||
cp "$settings_path" "$backup_path"
|
||||
|
||||
tmp_file="$(mktemp)"
|
||||
jq --arg cmd "$hook_command" '
|
||||
.hooks = (.hooks // {}) |
|
||||
.hooks.SessionStart = (
|
||||
(.hooks.SessionStart // [])
|
||||
| map(.hooks = ((.hooks // []) | map(select(.command != $cmd))))
|
||||
| map(select((.hooks // []) | length > 0))
|
||||
) |
|
||||
if ((.hooks.SessionStart // []) | length) == 0 then del(.hooks.SessionStart) else . end |
|
||||
if (.hooks | length) == 0 then del(.hooks) else . end
|
||||
' "$settings_path" > "$tmp_file"
|
||||
mv "$tmp_file" "$settings_path"
|
||||
|
||||
ok "Claude Code SessionStart hook 已移除"
|
||||
}
|
||||
|
||||
load_dependency_skills() {
|
||||
local dep
|
||||
|
||||
DEP_SKILLS=()
|
||||
|
||||
while IFS= read -r dep; do
|
||||
[ -n "$dep" ] && DEP_SKILLS+=("$dep")
|
||||
done < <(bash "$SOURCE_REGISTRY_SCRIPT" unique-dependencies bundled)
|
||||
}
|
||||
|
||||
join_source_labels() {
|
||||
local category="$1"
|
||||
|
||||
bash "$SOURCE_REGISTRY_SCRIPT" list-by-category "$category" \
|
||||
| awk -F '\t' '
|
||||
BEGIN { separator = "" }
|
||||
NF {
|
||||
printf "%s%s", separator, $2
|
||||
separator = "、"
|
||||
}
|
||||
END {
|
||||
if (separator == "") {
|
||||
printf "-"
|
||||
}
|
||||
printf "\n"
|
||||
}
|
||||
'
|
||||
}
|
||||
|
||||
run_cmd() {
|
||||
if [ "$DRY_RUN" -eq 1 ]; then
|
||||
printf '[dry-run] %s\n' "$*"
|
||||
return 0
|
||||
fi
|
||||
"$@"
|
||||
}
|
||||
|
||||
copy_item() {
|
||||
local source_path="$1"
|
||||
local target_path="$2"
|
||||
|
||||
if [ "$DRY_RUN" -eq 1 ]; then
|
||||
printf '[dry-run] copy %s -> %s\n' "$source_path" "$target_path"
|
||||
return 0
|
||||
fi
|
||||
|
||||
rm -rf "$target_path"
|
||||
cp -R "$source_path" "$target_path"
|
||||
}
|
||||
|
||||
detect_available_platforms() {
|
||||
local found=()
|
||||
|
||||
if [ -d "$HOME/.claude" ] || [ -d "$HOME/.claude/skills" ]; then
|
||||
found+=("claude")
|
||||
fi
|
||||
|
||||
if [ -d "$HOME/.codex" ] || [ -d "$HOME/.codex/skills" ] || [ -d "$HOME/.Codex" ] || [ -d "$HOME/.Codex/skills" ]; then
|
||||
found+=("codex")
|
||||
fi
|
||||
|
||||
if [ -d "$HOME/.openclaw" ] || [ -d "$HOME/.openclaw/skills" ]; then
|
||||
found+=("openclaw")
|
||||
fi
|
||||
|
||||
if [ -d "$HOME/.hermes" ] || [ -d "$HOME/.hermes/skills" ]; then
|
||||
found+=("hermes")
|
||||
fi
|
||||
|
||||
printf '%s\n' "${found[@]}"
|
||||
}
|
||||
|
||||
install_dependency_skills() {
|
||||
local skill_root="$1"
|
||||
local dep dep_target dep_source
|
||||
|
||||
for dep in "${DEP_SKILLS[@]}"; do
|
||||
dep_source="$SCRIPT_DIR/deps/$dep"
|
||||
dep_target="$skill_root/$dep"
|
||||
|
||||
if [ ! -d "$dep_source" ]; then
|
||||
warn "$dep:deps/ 中未找到源文件,跳过"
|
||||
continue
|
||||
fi
|
||||
|
||||
copy_item "$dep_source" "$dep_target"
|
||||
ok "$dep 已准备到 $dep_target"
|
||||
done
|
||||
}
|
||||
|
||||
install_companion_skills() {
|
||||
local platform="$1"
|
||||
local skill_root="$2"
|
||||
local skill_rel skill_source skill_target skill_name
|
||||
|
||||
while IFS= read -r skill_rel; do
|
||||
[ -n "$skill_rel" ] || continue
|
||||
|
||||
skill_source="$SCRIPT_DIR/$skill_rel"
|
||||
skill_name="$(basename "$skill_rel")"
|
||||
skill_target="$skill_root/$skill_name"
|
||||
|
||||
if [ ! -d "$skill_source" ]; then
|
||||
warn "$skill_name:仓库中未找到源文件,跳过"
|
||||
continue
|
||||
fi
|
||||
|
||||
copy_item "$skill_source" "$skill_target"
|
||||
ok "$skill_name 已安装到 $skill_target"
|
||||
done < <(list_companion_skill_sources "$platform")
|
||||
}
|
||||
|
||||
install_bundle() {
|
||||
local target_dir="$1"
|
||||
local item source_path target_path
|
||||
|
||||
for item in "${MANAGED_ITEMS[@]}"; do
|
||||
source_path="$SCRIPT_DIR/$item"
|
||||
target_path="$target_dir/$item"
|
||||
|
||||
if [ ! -e "$source_path" ]; then
|
||||
warn "$item:安装源文件缺失,跳过"
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ "$source_path" = "$target_path" ] && [ -e "$target_path" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
copy_item "$source_path" "$target_path"
|
||||
done
|
||||
|
||||
# 安装后校验:确保清单文件都已就位(Windows install.ps1 尤其重要)
|
||||
if [ "$DRY_RUN" -ne 1 ]; then
|
||||
for item in "${MANAGED_ITEMS[@]}"; do
|
||||
source_path="$SCRIPT_DIR/$item"
|
||||
target_path="$target_dir/$item"
|
||||
if [ -e "$source_path" ] && [ ! -e "$target_path" ]; then
|
||||
err "$item:已列入安装清单但未出现在目标目录,拷贝可能失败"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
ok "已校验全部 ${#MANAGED_ITEMS[@]} 项安装清单文件"
|
||||
fi
|
||||
}
|
||||
|
||||
install_node_deps() {
|
||||
local skill_root="$1"
|
||||
local baoyu_dir="$skill_root/baoyu-url-to-markdown/scripts"
|
||||
|
||||
if [ ! -d "$baoyu_dir" ] || [ ! -f "$baoyu_dir/package.json" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ -d "$baoyu_dir/node_modules" ]; then
|
||||
ok "baoyu-url-to-markdown 的 Node 依赖已存在"
|
||||
return 0
|
||||
fi
|
||||
|
||||
info "安装 baoyu-url-to-markdown 的 Node 依赖..."
|
||||
|
||||
if [ "$DRY_RUN" -eq 1 ]; then
|
||||
if command -v bun >/dev/null 2>&1; then
|
||||
printf '[dry-run] (cd %s && bun install)\n' "$baoyu_dir"
|
||||
elif command -v npm >/dev/null 2>&1; then
|
||||
printf '[dry-run] (cd %s && npm install)\n' "$baoyu_dir"
|
||||
else
|
||||
printf '[dry-run] 未找到 bun 或 npm,无法安装 Node 依赖\n'
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
if command -v bun >/dev/null 2>&1; then
|
||||
(cd "$baoyu_dir" && bun install) || warn "bun install 失败,跳过(可手动粘贴文本作为替代)"
|
||||
elif command -v npm >/dev/null 2>&1; then
|
||||
(cd "$baoyu_dir" && npm install) || warn "npm install 失败,跳过(可手动粘贴文本作为替代)"
|
||||
else
|
||||
warn "未找到 bun 或 npm,无法安装 Node 依赖"
|
||||
echo " 推荐安装 bun:curl -fsSL https://bun.sh/install | bash"
|
||||
return 0
|
||||
fi
|
||||
|
||||
[ -d "$baoyu_dir/node_modules" ] && ok "baoyu-url-to-markdown 的 Node 依赖安装完成"
|
||||
}
|
||||
|
||||
install_uv_tools() {
|
||||
if ! command -v uv >/dev/null 2>&1; then
|
||||
warn "未找到 uv,跳过 wechat-article-to-markdown 安装"
|
||||
echo " 安装 uv:curl -LsSf https://astral.sh/uv/install.sh | sh"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if command -v wechat-article-to-markdown >/dev/null 2>&1; then
|
||||
ok "wechat-article-to-markdown 已安装"
|
||||
return 0
|
||||
fi
|
||||
|
||||
info "安装 wechat-article-to-markdown..."
|
||||
|
||||
if [ "$DRY_RUN" -eq 1 ]; then
|
||||
printf '[dry-run] uv tool install %s\n' "${WECHAT_TOOL_URL}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
uv tool install "${WECHAT_TOOL_URL}" \
|
||||
|| warn "wechat-article-to-markdown 安装失败(可手动安装:uv tool install ${WECHAT_TOOL_URL})"
|
||||
|
||||
if command -v wechat-article-to-markdown >/dev/null 2>&1; then
|
||||
ok "wechat-article-to-markdown 安装完成"
|
||||
fi
|
||||
}
|
||||
|
||||
bootstrap_optional_adapters() {
|
||||
local skill_root="$1"
|
||||
|
||||
if [ "$WITH_OPTIONAL_ADAPTERS" -ne 1 ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
load_dependency_skills
|
||||
install_dependency_skills "$skill_root"
|
||||
install_node_deps "$skill_root"
|
||||
install_uv_tools
|
||||
}
|
||||
|
||||
check_environment() {
|
||||
echo ""
|
||||
echo "================================"
|
||||
echo " 环境检查"
|
||||
echo "================================"
|
||||
echo ""
|
||||
|
||||
if command -v uv >/dev/null 2>&1; then
|
||||
ok "uv 已安装(可安装 wechat-article-to-markdown,并运行 youtube-transcript)"
|
||||
else
|
||||
warn "未找到 uv。wechat-article-to-markdown 和 youtube-transcript 需要 uv"
|
||||
echo " 可用 Homebrew 安装:brew install uv"
|
||||
fi
|
||||
|
||||
if command -v wechat-article-to-markdown >/dev/null 2>&1; then
|
||||
ok "wechat-article-to-markdown 已可用"
|
||||
else
|
||||
warn "未找到 wechat-article-to-markdown。无法自动提取微信公众号"
|
||||
echo " 可手动安装:uv tool install ${WECHAT_TOOL_URL}"
|
||||
fi
|
||||
|
||||
if command -v lsof >/dev/null 2>&1 && lsof -i :9222 -sTCP:LISTEN >/dev/null 2>&1; then
|
||||
ok "Chrome 调试端口 9222 已监听(可复用已登录会话)"
|
||||
else
|
||||
info "未检测到 Chrome 调试端口 9222。baoyu-url-to-markdown 仍可自动拉起临时浏览器"
|
||||
echo " 如需复用已登录会话,再执行:open -na \"Google Chrome\" --args --remote-debugging-port=9222"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "提示:即使部分外挂不可用,PDF / 本地文件 / 纯文本仍可直接进入主线。"
|
||||
}
|
||||
|
||||
print_source_boundary() {
|
||||
local core_sources optional_sources manual_sources
|
||||
|
||||
core_sources="$(join_source_labels core_builtin)"
|
||||
optional_sources="$(join_source_labels optional_adapter)"
|
||||
manual_sources="$(join_source_labels manual_only)"
|
||||
|
||||
echo ""
|
||||
echo "================================"
|
||||
echo " 来源边界"
|
||||
echo "================================"
|
||||
echo ""
|
||||
echo "核心主线:$core_sources"
|
||||
echo "可选外挂:$optional_sources"
|
||||
echo "手动入口:$manual_sources"
|
||||
}
|
||||
|
||||
print_adapter_states() {
|
||||
local output
|
||||
|
||||
echo ""
|
||||
echo "================================"
|
||||
echo " 外挂状态"
|
||||
echo "================================"
|
||||
echo ""
|
||||
|
||||
output="$(
|
||||
bash "$ADAPTER_STATE_SCRIPT" --skill-root "$SKILL_ROOT" --layout-mode installed_skill summary-human 2>&1
|
||||
)" || {
|
||||
warn "无法生成外挂状态摘要"
|
||||
printf '%s\n' "$output"
|
||||
return 0
|
||||
}
|
||||
|
||||
printf '%s\n' "$output"
|
||||
}
|
||||
|
||||
print_optional_adapter_hint() {
|
||||
local command
|
||||
|
||||
command="bash install.sh"
|
||||
if [ "$UPGRADE" -eq 1 ]; then
|
||||
command="$command --upgrade"
|
||||
fi
|
||||
command="$command --platform ${PLATFORM}"
|
||||
if [ -n "$TARGET_DIR" ]; then
|
||||
command="$command --target-dir ${TARGET_DIR}"
|
||||
fi
|
||||
command="$command --with-optional-adapters"
|
||||
|
||||
echo ""
|
||||
echo "提示:当前只准备了知识库核心主线。"
|
||||
echo "如需网页 / X / 微信公众号 / YouTube / 知乎自动提取,再运行:"
|
||||
echo " $command"
|
||||
}
|
||||
|
||||
print_claude_upgrade_hint() {
|
||||
local platform="$1"
|
||||
|
||||
if [ "$platform" != "claude" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "提示:Claude Code 安装完成后,还可以直接用 /llm-wiki-upgrade 更新核心主线。"
|
||||
}
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--platform)
|
||||
[ $# -ge 2 ] || { err "--platform 需要一个值"; usage; exit 1; }
|
||||
PLATFORM="$2"
|
||||
PLATFORM_EXPLICIT=1
|
||||
shift 2
|
||||
;;
|
||||
--dry-run)
|
||||
DRY_RUN=1
|
||||
shift
|
||||
;;
|
||||
--target-dir)
|
||||
[ $# -ge 2 ] || { err "--target-dir 需要一个值"; usage; exit 1; }
|
||||
TARGET_DIR="$2"
|
||||
shift 2
|
||||
;;
|
||||
--with-optional-adapters)
|
||||
WITH_OPTIONAL_ADAPTERS=1
|
||||
shift
|
||||
;;
|
||||
--install-hooks)
|
||||
INSTALL_HOOKS=1
|
||||
shift
|
||||
;;
|
||||
--uninstall-hooks)
|
||||
UNINSTALL_HOOKS=1
|
||||
shift
|
||||
;;
|
||||
--upgrade)
|
||||
UPGRADE=1
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
err "未知参数:$1"
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "$INSTALL_HOOKS" -eq 1 ] && [ "$UNINSTALL_HOOKS" -eq 1 ]; then
|
||||
err "--install-hooks 和 --uninstall-hooks 不能同时使用"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$UPGRADE" -eq 1 ]; then
|
||||
if [ -n "$TARGET_DIR" ] && [ "$PLATFORM" = "auto" ]; then
|
||||
err "自定义目标目录升级时请显式传入 --platform"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$PLATFORM" = "auto" ]; then
|
||||
detected_platforms=()
|
||||
for p in claude codex openclaw hermes; do
|
||||
skill_root_candidate="$(resolve_platform_skill_root "$p")"
|
||||
[ -d "$skill_root_candidate/$SKILL_NAME" ] && detected_platforms+=("$p")
|
||||
done
|
||||
|
||||
if [ "${#detected_platforms[@]}" -eq 0 ]; then
|
||||
err "没有检测到已安装的 llm-wiki,请先运行安装"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "${#detected_platforms[@]}" -gt 1 ]; then
|
||||
err "检测到多个已安装平台:${detected_platforms[*]}。升级时请显式传入 --platform"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PLATFORM="${detected_platforms[0]}"
|
||||
UPGRADE_PLATFORMS=("${detected_platforms[@]}")
|
||||
else
|
||||
UPGRADE_PLATFORMS=("$PLATFORM")
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "================================"
|
||||
echo " llm-wiki 升级"
|
||||
echo "================================"
|
||||
echo ""
|
||||
|
||||
if [ -d "$SCRIPT_DIR/.git" ]; then
|
||||
info "从远程拉取最新代码..."
|
||||
if [ "$DRY_RUN" -eq 1 ]; then
|
||||
printf '[dry-run] git -C %s pull\n' "$SCRIPT_DIR"
|
||||
else
|
||||
git -C "$SCRIPT_DIR" pull || {
|
||||
err "git pull 失败,请检查网络或手动拉取后重试"
|
||||
exit 1
|
||||
}
|
||||
fi
|
||||
ok "代码已拉取到最新"
|
||||
else
|
||||
warn "当前目录不是 git 仓库,跳过 git pull"
|
||||
fi
|
||||
|
||||
upgrade_failures=0
|
||||
|
||||
for upgrade_platform in "${UPGRADE_PLATFORMS[@]}"; do
|
||||
upgrade_root="$(resolve_platform_skill_root "$upgrade_platform")"
|
||||
if [ -n "$TARGET_DIR" ]; then
|
||||
upgrade_target="$TARGET_DIR"
|
||||
upgrade_root="$(dirname "$upgrade_target")"
|
||||
else
|
||||
upgrade_target="$upgrade_root/$SKILL_NAME"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
info "更新 $upgrade_platform 的 llm-wiki..."
|
||||
echo " 目标目录:$upgrade_target"
|
||||
|
||||
if [ ! -d "$upgrade_target" ]; then
|
||||
err "$upgrade_platform 尚未安装 llm-wiki:$upgrade_target"
|
||||
upgrade_failures=$((upgrade_failures + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
run_cmd mkdir -p "$upgrade_target"
|
||||
install_bundle "$upgrade_target"
|
||||
install_companion_skills "$upgrade_platform" "$upgrade_root"
|
||||
bootstrap_optional_adapters "$upgrade_root"
|
||||
ok "$upgrade_platform 的 llm-wiki 已更新"
|
||||
done
|
||||
|
||||
if [ "$upgrade_failures" -gt 0 ]; then
|
||||
echo ""
|
||||
err "llm-wiki 升级失败,请先确认目标目录存在且已完成安装"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
print_source_boundary
|
||||
if [ "$WITH_OPTIONAL_ADAPTERS" -eq 1 ]; then
|
||||
check_environment
|
||||
else
|
||||
print_optional_adapter_hint
|
||||
fi
|
||||
print_claude_upgrade_hint "$PLATFORM"
|
||||
|
||||
echo ""
|
||||
ok "llm-wiki 升级完成"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$PLATFORM" = "auto" ] && { [ "$INSTALL_HOOKS" -eq 1 ] || [ "$UNINSTALL_HOOKS" -eq 1 ]; }; then
|
||||
PLATFORM="claude"
|
||||
fi
|
||||
|
||||
if [ "$PLATFORM" = "auto" ]; then
|
||||
detected_platforms=()
|
||||
while IFS= read -r platform_name; do
|
||||
[ -n "$platform_name" ] && detected_platforms+=("$platform_name")
|
||||
done < <(detect_available_platforms)
|
||||
if [ "${#detected_platforms[@]}" -eq 1 ]; then
|
||||
PLATFORM="${detected_platforms[0]}"
|
||||
elif [ "${#detected_platforms[@]}" -eq 0 ]; then
|
||||
err "没有检测到受支持的平台目录。请显式传入 --platform claude|codex|openclaw|hermes"
|
||||
exit 1
|
||||
else
|
||||
err "检测到多个可用平台:${detected_platforms[*]}。请显式传入 --platform"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
SKILL_ROOT="$(resolve_platform_skill_root "$PLATFORM")"
|
||||
|
||||
if { [ "$INSTALL_HOOKS" -eq 1 ] || [ "$UNINSTALL_HOOKS" -eq 1 ]; } && [ "$PLATFORM" != "claude" ]; then
|
||||
err "只有 Claude Code 支持 SessionStart hook"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -n "$TARGET_DIR" ]; then
|
||||
TARGET_SKILL_DIR="$TARGET_DIR"
|
||||
SKILL_ROOT="$(dirname "$TARGET_SKILL_DIR")"
|
||||
else
|
||||
TARGET_SKILL_DIR="$SKILL_ROOT/$SKILL_NAME"
|
||||
fi
|
||||
|
||||
if [ "$UNINSTALL_HOOKS" -eq 1 ]; then
|
||||
uninstall_claude_session_hook "$TARGET_SKILL_DIR"
|
||||
echo ""
|
||||
ok "llm-wiki hook 已移除"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$INSTALL_HOOKS" -eq 1 ] && [ "$PLATFORM_EXPLICIT" -eq 0 ] && [ -z "$TARGET_DIR" ]; then
|
||||
register_claude_session_hook "$TARGET_SKILL_DIR"
|
||||
echo ""
|
||||
ok "llm-wiki hook 已准备完成"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "================================"
|
||||
echo " llm-wiki 安装"
|
||||
echo "================================"
|
||||
echo ""
|
||||
echo "平台:$PLATFORM"
|
||||
echo "技能根目录:$SKILL_ROOT"
|
||||
echo "目标目录:$TARGET_SKILL_DIR"
|
||||
|
||||
run_cmd mkdir -p "$SKILL_ROOT"
|
||||
run_cmd mkdir -p "$TARGET_SKILL_DIR"
|
||||
|
||||
install_bundle "$TARGET_SKILL_DIR"
|
||||
install_companion_skills "$PLATFORM" "$SKILL_ROOT"
|
||||
bootstrap_optional_adapters "$SKILL_ROOT"
|
||||
print_source_boundary
|
||||
if [ "$WITH_OPTIONAL_ADAPTERS" -eq 1 ]; then
|
||||
check_environment
|
||||
print_adapter_states
|
||||
else
|
||||
print_optional_adapter_hint
|
||||
fi
|
||||
print_claude_upgrade_hint "$PLATFORM"
|
||||
|
||||
if [ "$INSTALL_HOOKS" -eq 1 ]; then
|
||||
register_claude_session_hook "$TARGET_SKILL_DIR"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
ok "llm-wiki 已准备完成"
|
||||
Reference in New Issue
Block a user